diff options
author | Martin Geisler <mgeisler@google.com> | 2024-04-25 15:25:11 +0200 |
---|---|---|
committer | Martin Geisler <mgeisler@google.com> | 2024-04-26 11:20:15 +0200 |
commit | 6b1e56537b60434209b70589bc290e650d03a316 (patch) | |
tree | b6123aa56bfe54ad95cd008e97560f7321fc53d0 | |
parent | 84c51004aa5d80f77a6f175fbcf18872aff3480f (diff) | |
download | mockall_derive-main.tar.gz |
Upgrade mockall_derive to 0.12.1main
This project was upgraded with external_updater.
Usage: tools/external_updater/updater.sh update external/rust/crates/mockall_derive
For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
Test: TreeHugger
Change-Id: I7fe1afc1dbbe4dc310ecd207ce4a66b895542f90
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Android.bp | 16 | ||||
-rw-r--r-- | Cargo.toml | 10 | ||||
-rw-r--r-- | METADATA | 22 | ||||
-rw-r--r-- | cargo_embargo.json | 4 | ||||
-rw-r--r-- | patches/use-deprecated-syn1-dep.patch | 21 | ||||
-rw-r--r-- | src/automock.rs | 180 | ||||
-rw-r--r-- | src/lib.rs | 389 | ||||
-rw-r--r-- | src/mock_function.rs | 665 | ||||
-rw-r--r-- | src/mock_item.rs | 91 | ||||
-rw-r--r-- | src/mock_item_struct.rs | 234 | ||||
-rw-r--r-- | src/mock_trait.rs | 80 | ||||
-rw-r--r-- | src/mockable_item.rs | 115 | ||||
-rw-r--r-- | src/mockable_struct.rs | 522 |
14 files changed, 1288 insertions, 1063 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 1232443..1690503 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "d5351f7215c6c5bca11f704ed41d9ae768b43007" + "sha1": "20e1c6d12b02e9af2e76a08b28ede4f4cd370726" }, "path_in_vcs": "mockall_derive" }
\ No newline at end of file @@ -1,5 +1,7 @@ // This file is generated by cargo_embargo. -// Do not modify this file as changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. +// Content before the first "rust_*" or "genrule" module is preserved. package { default_team: "trendy_team_android_rust", @@ -9,14 +11,14 @@ rust_proc_macro { name: "libmockall_derive", crate_name: "mockall_derive", cargo_env_compat: true, - cargo_pkg_version: "0.11.4", + cargo_pkg_version: "0.12.1", srcs: ["src/lib.rs"], - edition: "2018", + edition: "2021", rustlibs: [ "libcfg_if", "libproc_macro2", "libquote", - "libsyn_deprecated", + "libsyn", ], } @@ -24,18 +26,18 @@ rust_test_host { name: "mockall_derive_test_src_lib", crate_name: "mockall_derive", cargo_env_compat: true, - cargo_pkg_version: "0.11.4", + cargo_pkg_version: "0.12.1", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, test_options: { unit_test: true, }, - edition: "2018", + edition: "2021", rustlibs: [ "libcfg_if", "libproc_macro2", "libquote", - "libsyn_deprecated", + "libsyn", ], } @@ -10,9 +10,9 @@ # See Cargo.toml.orig for the original contents. [package] -edition = "2018" +edition = "2021" name = "mockall_derive" -version = "0.11.4" +version = "0.12.1" authors = ["Alan Somers <asomers@gmail.com>"] description = """ Procedural macros for Mockall @@ -25,7 +25,7 @@ keywords = [ "testing", ] categories = ["development-tools::testing"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" repository = "https://github.com/asomers/mockall" [package.metadata.release] @@ -39,13 +39,13 @@ proc-macro = true version = "1.0" [dependencies.proc-macro2] -version = "1.0" +version = "1.0.60" [dependencies.quote] version = "1.0" [dependencies.syn] -version = "1.0.87" +version = "2.0.9" features = [ "extra-traits", "full", @@ -1,20 +1,24 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update external/rust/crates/mockall_derive +# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md + name: "mockall_derive" description: "()" third_party { + license_type: NOTICE + last_upgrade_date { + year: 2024 + month: 4 + day: 25 + } identifier { type: "crates.io" - value: "https://crates.io/crates/mockall_derive" + value: "https://static.crates.io/crates/mockall_derive/mockall_derive-0.12.1.crate" + version: "0.11.4" } identifier { type: "Archive" value: "https://static.crates.io/crates/mockall_derive/mockall_derive-0.11.4.crate" - } - version: "0.11.4" - # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same. - license_type: NOTICE - last_upgrade_date { - year: 2023 - month: 11 - day: 6 + version: "0.12.1" } } diff --git a/cargo_embargo.json b/cargo_embargo.json index f14bd80..e86c1ef 100644 --- a/cargo_embargo.json +++ b/cargo_embargo.json @@ -1,12 +1,12 @@ { + "run_cargo": false, "tests": true, "package": { "mockall_derive": { "device_supported": false, "dep_blocklist": [ "libpretty_assertions" - ], - "patch": "patches/use-deprecated-syn1-dep.patch" + ] } } } diff --git a/patches/use-deprecated-syn1-dep.patch b/patches/use-deprecated-syn1-dep.patch deleted file mode 100644 index 5d85ecf..0000000 --- a/patches/use-deprecated-syn1-dep.patch +++ /dev/null @@ -1,21 +0,0 @@ -Patch Android.bp to use the old syn v1 package. - ---- a/Android.bp 2023-11-06 16:05:46.887866053 +0100 -+++ b/Android.bp 2023-11-06 16:06:15.768058832 +0100 -@@ -14,7 +14,7 @@ - "libcfg_if", - "libproc_macro2", - "libquote", -- "libsyn", -+ "libsyn_deprecated", - ], - } - -@@ -34,6 +34,6 @@ - "libcfg_if", - "libproc_macro2", - "libquote", -- "libsyn", -+ "libsyn_deprecated", - ], - } diff --git a/src/automock.rs b/src/automock.rs index bba6cb1..43c92ed 100644 --- a/src/automock.rs +++ b/src/automock.rs @@ -7,16 +7,13 @@ use syn::parse::{Parse, ParseStream}; // This enum is very short-lived, so it's fine not to box it. #[allow(clippy::large_enum_variant)] enum Attr { - Mod(ItemMod), Type(TraitItemType), } impl Parse for Attr { fn parse(input: ParseStream) -> parse::Result<Self> { let lookahead = input.lookahead1(); - if lookahead.peek(Token![mod]) { - input.parse().map(Attr::Mod) - } else if lookahead.peek(Token![type]) { + if lookahead.peek(Token![type]) { input.parse().map(Attr::Type) } else { Err(lookahead.error()) @@ -28,7 +25,6 @@ impl Parse for Attr { #[derive(Debug, Default)] pub(crate) struct Attrs { pub attrs: HashMap<Ident, Type>, - pub modname: Option<Ident>, } impl Attrs { @@ -46,13 +42,11 @@ impl Attrs { } pub(crate) fn substitute_item_impl(&self, item_impl: &mut ItemImpl) { - let (_, trait_path, _) = item_impl - .trait_ - .as_ref() + let (_, trait_path, _) = item_impl.trait_.as_ref() .expect("Should only be called for trait item impls"); let trait_ident = find_ident_from_path(trait_path).0; for item in item_impl.items.iter_mut() { - if let ImplItem::Method(method) = item { + if let ImplItem::Fn(method) = item { let sig = &mut method.sig; for fn_arg in sig.inputs.iter_mut() { if let FnArg::Typed(arg) = fn_arg { @@ -66,43 +60,57 @@ impl Attrs { } } - fn substitute_path_segment(&self, seg: &mut PathSegment, traitname: &Ident) { + fn substitute_path_segment(&self, seg: &mut PathSegment, traitname: &Ident) + { match &mut seg.arguments { - PathArguments::None => - /* nothing to do */ - { - () - } + PathArguments::None => /* nothing to do */(), PathArguments::Parenthesized(p) => { compile_error(p.span(), "Mockall does not support mocking Fn objects. See https://github.com/asomers/mockall/issues/139"); - } + }, PathArguments::AngleBracketed(abga) => { for arg in abga.args.iter_mut() { match arg { - GenericArgument::Type(ty) => self.substitute_type(ty, traitname), - GenericArgument::Binding(binding) => { - self.substitute_type(&mut binding.ty, traitname); - } - _ => { + GenericArgument::Lifetime(_) => { /* * Nothing to do, as long as lifetimes can't be * associated types */ } + GenericArgument::Type(ty) => { + self.substitute_type(ty, traitname) + }, + GenericArgument::AssocConst(_) => { + // Nothing to do + } + GenericArgument::AssocType(at) => { + self.substitute_type(&mut at.ty, traitname); + } + // TODO: Constraints + _ => { + // Not handled. Hopefully doing nothing works. + } } } - } + }, } } /// Recursively substitute types in the input fn substitute_type(&self, ty: &mut Type, traitname: &Ident) { match ty { - Type::Slice(s) => self.substitute_type(s.elem.as_mut(), traitname), - Type::Array(a) => self.substitute_type(a.elem.as_mut(), traitname), - Type::Ptr(p) => self.substitute_type(p.elem.as_mut(), traitname), - Type::Reference(r) => self.substitute_type(r.elem.as_mut(), traitname), + Type::Slice(s) => { + self.substitute_type(s.elem.as_mut(), traitname) + }, + Type::Array(a) => { + self.substitute_type(a.elem.as_mut(), traitname) + }, + Type::Ptr(p) => { + self.substitute_type(p.elem.as_mut(), traitname) + }, + Type::Reference(r) => { + self.substitute_type(r.elem.as_mut(), traitname) + }, Type::BareFn(bfn) => { for fn_arg in bfn.inputs.iter_mut() { self.substitute_type(&mut fn_arg.ty, traitname); @@ -110,7 +118,7 @@ impl Attrs { if let ReturnType::Type(_, ref mut ty) = &mut bfn.output { self.substitute_type(ty, traitname); } - } + }, Type::Tuple(tuple) => { for elem in tuple.elems.iter_mut() { self.substitute_type(elem, traitname) @@ -127,9 +135,9 @@ impl Attrs { if qself.position != 1 || qp.segments.len() != 1 || path.path.segments.len() != 2 - || qident != "Self" - { - compile_error(path.span(), "QSelf is a work in progress"); + || qident != "Self" { + compile_error(path.span(), + "QSelf is a work in progress"); } let mut seg_iter = path.path.segments.iter().rev(); @@ -146,7 +154,8 @@ impl Attrs { if let Some(new_type) = self.attrs.get(to_sub) { *ty = new_type.clone(); } else { - compile_error(to_sub.span(), "Unknown type substitution for QSelf"); + compile_error(to_sub.span(), + "Unknown type substitution for QSelf"); } } else if let Some(newty) = self.get_path(&path.path) { *ty = newty; @@ -155,43 +164,51 @@ impl Attrs { self.substitute_path_segment(seg, traitname); } } - } + }, Type::TraitObject(to) => { for bound in to.bounds.iter_mut() { self.substitute_type_param_bound(bound, traitname); } - } + }, Type::ImplTrait(it) => { for bound in it.bounds.iter_mut() { self.substitute_type_param_bound(bound, traitname); } - } - Type::Paren(p) => self.substitute_type(p.elem.as_mut(), traitname), - Type::Group(g) => self.substitute_type(g.elem.as_mut(), traitname), + }, + Type::Paren(p) => { + self.substitute_type(p.elem.as_mut(), traitname) + }, + Type::Group(g) => { + self.substitute_type(g.elem.as_mut(), traitname) + }, Type::Macro(_) | Type::Verbatim(_) => { - compile_error( - ty.span(), - "mockall_derive does not support this type when using associated types", - ); - } - Type::Infer(_) | Type::Never(_) => { /* Nothing to do */ } + compile_error(ty.span(), + "mockall_derive does not support this type when using associated types"); + }, + Type::Infer(_) | Type::Never(_) => { + /* Nothing to do */ + }, _ => compile_error(ty.span(), "Unsupported type"), } } - fn substitute_type_param_bound(&self, bound: &mut TypeParamBound, traitname: &Ident) { + fn substitute_type_param_bound(&self, + bound: &mut TypeParamBound, + traitname: &Ident) + { if let TypeParamBound::Trait(t) = bound { match self.get_path(&t.path) { None => { for seg in t.path.segments.iter_mut() { self.substitute_path_segment(seg, traitname); } - } + }, Some(Type::Path(type_path)) => { t.path = type_path.path; - } + }, Some(_) => { - compile_error(t.path.span(), "Can only substitute paths for trait bounds"); + compile_error(t.path.span(), + "Can only substitute paths for trait bounds"); } } } @@ -209,10 +226,11 @@ impl Attrs { // bounds tity.bounds = Punctuated::new(); } else { - compile_error(tity.span(), "Default value not given for associated type"); + compile_error(tity.span(), + "Default value not given for associated type"); } - } - TraitItem::Method(method) => { + }, + TraitItem::Fn(method) => { let sig = &mut method.sig; for fn_arg in sig.inputs.iter_mut() { if let FnArg::Typed(arg) = fn_arg { @@ -222,7 +240,7 @@ impl Attrs { if let ReturnType::Type(_, ref mut ty) = &mut sig.output { self.substitute_type(ty, &item.ident); } - } + }, _ => { // Nothing to do } @@ -235,33 +253,21 @@ impl Attrs { impl Parse for Attrs { fn parse(input: ParseStream) -> parse::Result<Self> { let mut attrs = HashMap::new(); - let mut modname = None; while !input.is_empty() { let attr: Attr = input.parse()?; match attr { - Attr::Mod(item_mod) => { - if let Some((br, _)) = item_mod.content { - compile_error( - br.span, - "mod name attributes must have the form \"mod my_name;\"", - ); - } - modname = Some(item_mod.ident.clone()); - } Attr::Type(trait_item_type) => { let ident = trait_item_type.ident.clone(); if let Some((_, ty)) = trait_item_type.default { attrs.insert(ident, ty.clone()); } else { - compile_error( - trait_item_type.span(), - "automock type attributes must have a default value", - ); + compile_error(trait_item_type.span(), + "automock type attributes must have a default value"); } } } } - Ok(Attrs { attrs, modname }) + Ok(Attrs{attrs}) } } @@ -274,8 +280,8 @@ mod t { attrs: TokenStream, input: TokenStream, traitname: Ident, - expected: TokenStream, - ) { + expected: TokenStream) + { let _self: super::Attrs = parse2(attrs).unwrap(); let mut in_ty: Type = parse2(input).unwrap(); let expect_ty: Type = parse2(expected).unwrap(); @@ -285,41 +291,27 @@ mod t { #[test] fn qself() { - check_substitute_type( - quote!( - type T = u32; - ), - quote!(<Self as Foo>::T), - format_ident!("Foo"), - quote!(u32), - ); + check_substitute_type(quote!(type T = u32;), + quote!(<Self as Foo>::T), + format_ident!("Foo"), + quote!(u32)); } #[test] - #[should_panic( - expected = "Mockall does not support QSelf substitutions except for the trait being mocked" - )] + #[should_panic(expected = "Mockall does not support QSelf substitutions except for the trait being mocked")] fn qself_other() { - check_substitute_type( - quote!( - type T = u32; - ), - quote!(<Self as AsRef>::T), - format_ident!("Foo"), - quote!(u32), - ); + check_substitute_type(quote!(type T = u32;), + quote!(<Self as AsRef>::T), + format_ident!("Foo"), + quote!(u32)); } #[test] #[should_panic(expected = "Unknown type substitution for QSelf")] fn unknown_substitution() { - check_substitute_type( - quote!( - type T = u32; - ), - quote!(<Self as Foo>::Q), - format_ident!("Foo"), - quote!(u32), - ); + check_substitute_type(quote!(type T = u32;), + quote!(<Self as Foo>::Q), + format_ident!("Foo"), + quote!(u32)); } } @@ -52,11 +52,155 @@ cfg_if! { } } else { fn compile_error(_span: Span, msg: &str) { - panic!("{}. More information may be available when mockall is built with the \"nightly\" feature.", msg); + panic!("{msg}. More information may be available when mockall is built with the \"nightly\" feature."); } } } +/// Does this Attribute represent Mockall's "concretize" pseudo-attribute? +fn is_concretize(attr: &Attribute) -> bool { + if attr.path().segments.last().unwrap().ident == "concretize" { + true + } else if attr.path().is_ident("cfg_attr") { + match &attr.meta { + Meta::List(ml) => { + ml.tokens.to_string().contains("concretize") + }, + // cfg_attr should always contain a list + _ => false, + } + } else { + false + } +} + +/// replace generic arguments with concrete trait object arguments +fn concretize_args(gen: &Generics, args: &Punctuated<FnArg, Token![,]>) -> + (Generics, Vec<FnArg>, Vec<TokenStream>) +{ + let mut hm = HashMap::default(); + + let mut save_types = |ident: &Ident, tpb: &Punctuated<TypeParamBound, Token![+]>| { + if !tpb.is_empty() { + if let Ok(newty) = parse2::<Type>(quote!(&(dyn #tpb))) { + // substitute T arguments + let subst_ty: Type = parse2(quote!(#ident)).unwrap(); + hm.insert(subst_ty, (newty.clone(), None)); + + // substitute &T arguments + let subst_ty: Type = parse2(quote!(&#ident)).unwrap(); + hm.insert(subst_ty, (newty, None)); + } else { + compile_error(tpb.span(), + "Type cannot be made into a trait object"); + } + + if let Ok(newty) = parse2::<Type>(quote!(&mut (dyn #tpb))) { + // substitute &mut T arguments + let subst_ty: Type = parse2(quote!(&mut #ident)).unwrap(); + hm.insert(subst_ty, (newty, None)); + } else { + compile_error(tpb.span(), + "Type cannot be made into a trait object"); + } + + // I wish we could substitute &[T] arguments. But there's no way + // for the mock method to turn &[T] into &[&dyn T]. + if let Ok(newty) = parse2::<Type>(quote!(&[&(dyn #tpb)])) { + let subst_ty: Type = parse2(quote!(&[#ident])).unwrap(); + hm.insert(subst_ty, (newty, Some(tpb.clone()))); + } else { + compile_error(tpb.span(), + "Type cannot be made into a trait object"); + } + } + }; + + for g in gen.params.iter() { + if let GenericParam::Type(tp) = g { + save_types(&tp.ident, &tp.bounds); + // else there had better be a where clause + } + } + if let Some(wc) = &gen.where_clause { + for pred in wc.predicates.iter() { + if let WherePredicate::Type(pt) = pred { + let bounded_ty = &pt.bounded_ty; + if let Ok(ident) = parse2::<Ident>(quote!(#bounded_ty)) { + save_types(&ident, &pt.bounds); + } else { + // We can't yet handle where clauses this complicated + } + } + } + } + + let outg = Generics { + lt_token: None, + gt_token: None, + params: Punctuated::new(), + where_clause: None + }; + let outargs: Vec<FnArg> = args.iter().map(|arg| { + if let FnArg::Typed(pt) = arg { + let mut immutable_pt = pt.clone(); + demutify_arg(&mut immutable_pt); + if let Some((newty, _)) = hm.get(&pt.ty) { + FnArg::Typed(PatType { + attrs: Vec::default(), + pat: immutable_pt.pat, + colon_token: pt.colon_token, + ty: Box::new(newty.clone()) + }) + } else { + FnArg::Typed(PatType { + attrs: Vec::default(), + pat: immutable_pt.pat, + colon_token: pt.colon_token, + ty: pt.ty.clone() + }) + } + } else { + arg.clone() + } + }).collect(); + + // Finally, Reference any concretizing arguments + // use filter_map to remove the &self argument + let call_exprs = args.iter().filter_map(|arg| { + match arg { + FnArg::Typed(pt) => { + let mut pt2 = pt.clone(); + demutify_arg(&mut pt2); + let pat = &pt2.pat; + if pat_is_self(pat) { + None + } else if let Some((_, newbound)) = hm.get(&pt.ty) { + if let Type::Reference(tr) = &*pt.ty { + if let Type::Slice(_ts) = &*tr.elem { + // Assume _ts is the generic type or we wouldn't be + // here + Some(quote!( + &(0..#pat.len()) + .map(|__mockall_i| &#pat[__mockall_i] as &(dyn #newbound)) + .collect::<Vec<_>>() + )) + } else { + Some(quote!(#pat)) + } + } else { + Some(quote!(&#pat)) + } + } else { + Some(quote!(#pat)) + } + }, + FnArg::Receiver(_) => None, + } + }).collect(); + (outg, outargs, call_exprs) +} + fn deanonymize_lifetime(lt: &mut Lifetime) { if lt.ident == "_" { lt.ident = format_ident!("static"); @@ -115,6 +259,7 @@ fn deanonymize(literal_type: &mut Type) { match tpb { TypeParamBound::Trait(tb) => deanonymize_path(&mut tb.path), TypeParamBound::Lifetime(lt) => deanonymize_lifetime(lt), + _ => () } } }, @@ -392,8 +537,8 @@ fn deselfify_path(path: &mut Path, actual: &Ident, generics: &Generics) { match arg { GenericArgument::Type(ty) => deselfify(ty, actual, generics), - GenericArgument::Binding(b) => - deselfify(&mut b.ty, actual, generics), + GenericArgument::AssocType(at) => + deselfify(&mut at.ty, actual, generics), _ => /* Nothing to do */(), } } @@ -469,8 +614,13 @@ fn deselfify_args( generics: &Generics) { for arg in args.iter_mut() { - if let FnArg::Typed(pt) = arg { - deselfify(pt.ty.as_mut(), actual, generics) + match arg { + FnArg::Receiver(r) => { + if r.colon_token.is_some() { + deselfify(r.ty.as_mut(), actual, generics) + } + }, + FnArg::Typed(pt) => deselfify(pt.ty.as_mut(), actual, generics) } } } @@ -494,6 +644,7 @@ fn find_lifetimes_in_tpb(bound: &TypeParamBound) -> HashSet<Lifetime> { TypeParamBound::Trait(tb) => { ret.extend(find_lifetimes_in_path(&tb.path)); }, + _ => () }; ret } @@ -510,15 +661,16 @@ fn find_lifetimes_in_path(path: &Path) -> HashSet<Lifetime> { GenericArgument::Type(ty) => { ret.extend(find_lifetimes(ty)); }, - GenericArgument::Binding(b) => { - ret.extend(find_lifetimes(&b.ty)); + GenericArgument::AssocType(at) => { + ret.extend(find_lifetimes(&at.ty)); }, GenericArgument::Constraint(c) => { for bound in c.bounds.iter() { ret.extend(find_lifetimes_in_tpb(bound)); } }, - GenericArgument::Const(_) => () + GenericArgument::Const(_) => (), + _ => () } } } @@ -609,10 +761,12 @@ impl<'a> AttrFormatter<'a> { #[allow(clippy::if_same_then_else)] fn format(&mut self) -> Vec<Attribute> { self.attrs.iter() - .cloned() .filter(|attr| { - let i = attr.path.get_ident(); - if i.is_none() { + let i = attr.path().segments.last().map(|ps| &ps.ident); + if is_concretize(attr) { + // Internally used attribute. Never emit. + false + } else if i.is_none() { false } else if *i.as_ref().unwrap() == "derive" { // We can't usefully derive any traits. Ignore them @@ -626,10 +780,15 @@ impl<'a> AttrFormatter<'a> { // ignore this attribute. // https://docs.rs/tracing/0.1.23/tracing/attr.instrument.html false + } else if *i.as_ref().unwrap() == "link_name" { + // This shows up sometimes when mocking ffi functions. We + // must not emit it on anything that isn't an ffi definition + false } else { true } - }).collect() + }).cloned() + .collect() } } @@ -653,8 +812,8 @@ fn supersuperfy_path(path: &mut Path, levels: usize) -> usize { GenericArgument::Type(ref mut ty) => { *ty = supersuperfy(ty, levels); }, - GenericArgument::Binding(ref mut binding) => { - binding.ty = supersuperfy(&binding.ty, levels); + GenericArgument::AssocType(ref mut at) => { + at.ty = supersuperfy(&at.ty, levels); }, GenericArgument::Constraint(ref mut constraint) => { supersuperfy_bounds(&mut constraint.bounds, levels); @@ -820,9 +979,9 @@ fn gen_mock_ident(ident: &Ident) -> Ident { /// "__mock_Foo" fn gen_mod_ident(struct_: &Ident, trait_: Option<&Ident>) -> Ident { if let Some(t) = trait_ { - format_ident!("__mock_{}_{}", struct_, t) + format_ident!("__mock_{struct_}_{}", t) } else { - format_ident!("__mock_{}", struct_) + format_ident!("__mock_{struct_}") } } @@ -848,7 +1007,6 @@ fn merge_generics(x: &Generics, y: &Generics) -> Generics { match (x, y) { (Type(xpt), Type(ypt)) => xpt.bounded_ty == ypt.bounded_ty, (Lifetime(xpl), Lifetime(ypl)) => xpl.lifetime == ypl.lifetime, - (Eq(xeq), Eq(yeq)) => xeq.lhs_ty == yeq.lhs_ty, _ => false } } @@ -921,14 +1079,20 @@ fn merge_generics(x: &Generics, y: &Generics) -> Generics { out } +fn lifetimes_to_generic_params(lv: &Punctuated<LifetimeParam, Token![,]>) + -> Punctuated<GenericParam, Token![,]> +{ + lv.iter() + .map(|lt| GenericParam::Lifetime(lt.clone())) + .collect() +} + /// Transform a Vec of lifetimes into a Generics -fn lifetimes_to_generics(lv: &Punctuated<LifetimeDef, Token![,]>)-> Generics { +fn lifetimes_to_generics(lv: &Punctuated<LifetimeParam, Token![,]>)-> Generics { if lv.is_empty() { Generics::default() } else { - let params = lv.iter() - .map(|lt| GenericParam::Lifetime(lt.clone())) - .collect(); + let params = lifetimes_to_generic_params(lv); Generics { lt_token: Some(Token![<](lv[0].span())), gt_token: Some(Token![>](lv[0].span())), @@ -946,8 +1110,8 @@ fn split_lifetimes( args: &[FnArg], rt: &ReturnType) -> (Generics, - Punctuated<LifetimeDef, token::Comma>, - Punctuated<LifetimeDef, token::Comma>) + Punctuated<LifetimeParam, token::Comma>, + Punctuated<LifetimeParam, token::Comma>) { if generics.lt_token.is_none() { return (generics, Default::default(), Default::default()); @@ -1041,14 +1205,14 @@ fn expectation_visibility(vis: &Visibility, levels: usize) // self => in super::super // in anything_else => super::super::anything_else if vr.path.segments.first().unwrap().ident == "crate" { - vr.clone().into() + Visibility::Restricted(vr.clone()) } else { let mut out = vr.clone(); out.in_token = Some(in_token); for _ in 0..levels { out.path.segments.insert(0, super_token.into()); } - out.into() + Visibility::Restricted(out) } }, _ => vis.clone() @@ -1069,7 +1233,7 @@ fn mock_it<M: Into<MockableItem>>(inputs: M) -> TokenStream let mock = MockItem::from(mockable); let ts = mock.into_token_stream(); if env::var("MOCKALL_DEBUG").is_ok() { - println!("{}", ts); + println!("{ts}"); } ts } @@ -1097,6 +1261,16 @@ fn do_mock(input: TokenStream) -> TokenStream do_mock_once(input) } +#[proc_macro_attribute] +pub fn concretize( + _attrs: proc_macro::TokenStream, + input: proc_macro::TokenStream) -> proc_macro::TokenStream +{ + // Do nothing. This "attribute" is processed as text by the real proc + // macros. + input +} + #[proc_macro] pub fn mock(input: proc_macro::TokenStream) -> proc_macro::TokenStream { do_mock(input.into()).into() @@ -1163,7 +1337,7 @@ mod mock { #[test] fn inherent_method_visibility() { - let code = r#" + let code = " Foo { fn foo(&self); pub fn bar(&self); @@ -1171,7 +1345,7 @@ mod mock { pub(super) fn bean(&self); pub(in crate::outer) fn boom(&self); } - "#; + "; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let output = do_mock(ts).to_string(); assert_not_contains(&output, quote!(pub fn foo)); @@ -1192,7 +1366,7 @@ mod mock { #[test] fn specific_impl() { - let code = r#" + let code = " pub Foo<T: 'static> {} impl Bar for Foo<u32> { fn bar(&self); @@ -1200,7 +1374,7 @@ mod mock { impl Bar for Foo<i32> { fn bar(&self); } - "#; + "; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let output = do_mock(ts).to_string(); assert_contains(&output, quote!(impl Bar for MockFoo<u32>)); @@ -1229,12 +1403,12 @@ mod automock { #[test] fn doc_comments() { - let code = r#" + let code = " mod foo { /// Function docs pub fn bar() { unimplemented!() } } - "#; + "; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); let output = do_automock(attrs_ts, ts).to_string(); @@ -1243,14 +1417,14 @@ mod automock { #[test] fn method_visibility() { - let code = r#" + let code = " impl Foo { fn foo(&self) {} pub fn bar(&self) {} pub(super) fn baz(&self) {} pub(crate) fn bang(&self) {} pub(in super::x) fn bean(&self) {} - }"#; + }"; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); let output = do_automock(attrs_ts, ts).to_string(); @@ -1271,7 +1445,7 @@ mod automock { #[test] #[should_panic(expected = "can only mock inline modules")] fn external_module() { - let code = r#"mod foo;"#; + let code = "mod foo;"; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); do_automock(attrs_ts, ts).to_string(); @@ -1279,9 +1453,9 @@ mod automock { #[test] fn trait_visibility() { - let code = r#" + let code = " pub(super) trait Foo {} - "#; + "; let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let output = do_automock(attrs_ts, ts).to_string(); @@ -1289,6 +1463,114 @@ mod automock { } } +mod concretize_args { + use super::*; + + fn check_concretize( + sig: TokenStream, + expected_inputs: &[TokenStream], + expected_call_exprs: &[TokenStream]) + { + let f: Signature = parse2(sig).unwrap(); + let (generics, inputs, call_exprs) = + concretize_args(&f.generics, &f.inputs); + assert!(generics.params.is_empty()); + assert_eq!(inputs.len(), expected_inputs.len()); + assert_eq!(call_exprs.len(), expected_call_exprs.len()); + for i in 0..inputs.len() { + let actual = &inputs[i]; + let exp = &expected_inputs[i]; + assert_eq!(quote!(#actual).to_string(), quote!(#exp).to_string()); + } + for i in 0..call_exprs.len() { + let actual = &call_exprs[i]; + let exp = &expected_call_exprs[i]; + assert_eq!(quote!(#actual).to_string(), quote!(#exp).to_string()); + } + } + + #[test] + fn bystanders() { + check_concretize( + quote!(fn foo<P: AsRef<Path>>(x: i32, p: P, y: &f64)), + &[quote!(x: i32), quote!(p: &(dyn AsRef<Path>)), quote!(y: &f64)], + &[quote!(x), quote!(&p), quote!(y)] + ); + } + + #[test] + fn multi_bounds() { + check_concretize( + quote!(fn foo<P: AsRef<String> + AsMut<String>>(p: P)), + &[quote!(p: &(dyn AsRef<String> + AsMut<String>))], + &[quote!(&p)] + ); + } + + #[test] + fn mutable_reference_arg() { + check_concretize( + quote!(fn foo<P: AsMut<Path>>(p: &mut P)), + &[quote!(p: &mut (dyn AsMut<Path>))], + &[quote!(p)] + ); + } + + #[test] + fn mutable_reference_multi_bounds() { + check_concretize( + quote!(fn foo<P: AsRef<String> + AsMut<String>>(p: &mut P)), + &[quote!(p: &mut (dyn AsRef<String> + AsMut<String>))], + &[quote!(p)] + ); + } + + #[test] + fn reference_arg() { + check_concretize( + quote!(fn foo<P: AsRef<Path>>(p: &P)), + &[quote!(p: &(dyn AsRef<Path>))], + &[quote!(p)] + ); + } + + #[test] + fn simple() { + check_concretize( + quote!(fn foo<P: AsRef<Path>>(p: P)), + &[quote!(p: &(dyn AsRef<Path>))], + &[quote!(&p)] + ); + } + + #[test] + fn slice() { + check_concretize( + quote!(fn foo<P: AsRef<Path>>(p: &[P])), + &[quote!(p: &[&(dyn AsRef<Path>)])], + &[quote!(&(0..p.len()).map(|__mockall_i| &p[__mockall_i] as &(dyn AsRef<Path>)).collect::<Vec<_>>())] + ); + } + + #[test] + fn slice_with_multi_bounds() { + check_concretize( + quote!(fn foo<P: AsRef<Path> + AsMut<String>>(p: &[P])), + &[quote!(p: &[&(dyn AsRef<Path> + AsMut<String>)])], + &[quote!(&(0..p.len()).map(|__mockall_i| &p[__mockall_i] as &(dyn AsRef<Path> + AsMut<String>)).collect::<Vec<_>>())] + ); + } + + #[test] + fn where_clause() { + check_concretize( + quote!(fn foo<P>(p: P) where P: AsRef<Path>), + &[quote!(p: &(dyn AsRef<Path>))], + &[quote!(&p)] + ); + } +} + mod deimplify { use super::*; @@ -1363,6 +1645,15 @@ mod deselfify { } #[test] + fn arc() { + check_deselfify( + quote!(Arc<Self>), + quote!(Foo), + quote!(), + quote!(Arc<Foo>) + ); + } + #[test] fn future() { check_deselfify( quote!(Box<dyn Future<Output=Self>>), @@ -1409,11 +1700,11 @@ mod dewhereselfify { #[test] fn lifetime() { - let mut meth: ImplItemMethod = parse2(quote!( - fn foo<'a>(&self) where 'a: 'static, Self: Sized; + let mut meth: ImplItemFn = parse2(quote!( + fn foo<'a>(&self) where 'a: 'static, Self: Sized {} )).unwrap(); - let expected: ImplItemMethod = parse2(quote!( - fn foo<'a>(&self) where 'a: 'static; + let expected: ImplItemFn = parse2(quote!( + fn foo<'a>(&self) where 'a: 'static {} )).unwrap(); dewhereselfify(&mut meth.sig.generics); assert_eq!(meth, expected); @@ -1421,11 +1712,11 @@ mod dewhereselfify { #[test] fn normal_method() { - let mut meth: ImplItemMethod = parse2(quote!( - fn foo(&self) where Self: Sized; + let mut meth: ImplItemFn = parse2(quote!( + fn foo(&self) where Self: Sized {} )).unwrap(); - let expected: ImplItemMethod = parse2(quote!( - fn foo(&self); + let expected: ImplItemFn = parse2(quote!( + fn foo(&self) {} )).unwrap(); dewhereselfify(&mut meth.sig.generics); assert_eq!(meth, expected); @@ -1433,11 +1724,11 @@ mod dewhereselfify { #[test] fn with_real_generics() { - let mut meth: ImplItemMethod = parse2(quote!( - fn foo<T>(&self, t: T) where Self: Sized, T: Copy; + let mut meth: ImplItemFn = parse2(quote!( + fn foo<T>(&self, t: T) where Self: Sized, T: Copy {} )).unwrap(); - let expected: ImplItemMethod = parse2(quote!( - fn foo<T>(&self, t: T) where T: Copy; + let expected: ImplItemFn = parse2(quote!( + fn foo<T>(&self, t: T) where T: Copy {} )).unwrap(); dewhereselfify(&mut meth.sig.generics); assert_eq!(meth, expected); diff --git a/src/mock_function.rs b/src/mock_function.rs index 86a8690..86e45e6 100644 --- a/src/mock_function.rs +++ b/src/mock_function.rs @@ -58,10 +58,14 @@ fn destrify(ty: &mut Type) { let osstring_ty: Type = parse2(quote!(::std::ffi::OsString)).unwrap(); match tr.elem.as_ref() { - Type::Path(ref path) if *path == cstr_ty => *tr.elem = cstring_ty, - Type::Path(ref path) if *path == osstr_ty => *tr.elem = osstring_ty, - Type::Path(ref path) if *path == path_ty => *tr.elem = pathbuf_ty, - Type::Path(ref path) if *path == str_ty => *tr.elem = string_ty, + Type::Path(ref path) if *path == cstr_ty => + *tr.elem = cstring_ty, + Type::Path(ref path) if *path == osstr_ty => + *tr.elem = osstring_ty, + Type::Path(ref path) if *path == path_ty => + *tr.elem = pathbuf_ty, + Type::Path(ref path) if *path == str_ty => + *tr.elem = string_ty, Type::Slice(ts) => { let inner = (*ts.elem).clone(); let mut segments = Punctuated::new(); @@ -70,22 +74,24 @@ fn destrify(ty: &mut Type) { let mut v: PathSegment = format_ident!("Vec").into(); let mut abga_args = Punctuated::new(); abga_args.push(GenericArgument::Type(inner)); - v.arguments = PathArguments::AngleBracketed(AngleBracketedGenericArguments { - colon2_token: None, - lt_token: Token![<](Span::call_site()), - args: abga_args, - gt_token: Token![>](Span::call_site()), - }); + v.arguments = PathArguments::AngleBracketed( + AngleBracketedGenericArguments { + colon2_token: None, + lt_token: Token![<](Span::call_site()), + args: abga_args, + gt_token: Token![>](Span::call_site()), + } + ); segments.push(v); *tr.elem = Type::Path(TypePath { qself: None, path: Path { leading_colon: Some(Token![::](Span::call_site())), - segments, - }, + segments + } }); - } + }, _ => (), // Nothing to do }; } @@ -94,10 +100,7 @@ fn destrify(ty: &mut Type) { /// Return the owned version of the input. fn ownify(ty: &Type) -> Type { if let Type::Reference(ref tr) = &ty { - if tr - .lifetime - .as_ref() - .map_or(false, |lt| lt.ident == "static") + if tr.lifetime.as_ref().map_or(false, |lt| lt.ident == "static") { // Just a static expectation ty.clone() @@ -116,29 +119,31 @@ fn send_syncify(wc: &mut Option<WhereClause>, bounded_ty: Type) { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: None, - path: Path::from(format_ident!("Send")), + path: Path::from(format_ident!("Send")) })); bounds.push(TypeParamBound::Trait(TraitBound { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: None, - path: Path::from(format_ident!("Sync")), + path: Path::from(format_ident!("Sync")) })); if wc.is_none() { *wc = Some(WhereClause { where_token: <Token![where]>::default(), - predicates: Punctuated::new(), + predicates: Punctuated::new() }); } - wc.as_mut() - .unwrap() - .predicates - .push(WherePredicate::Type(PredicateType { - lifetimes: None, - bounded_ty, - colon_token: Default::default(), - bounds, - })); + wc.as_mut().unwrap() + .predicates.push( + WherePredicate::Type( + PredicateType { + lifetimes: None, + bounded_ty, + colon_token: Default::default(), + bounds + } + ) + ); } /// Build a MockFunction. @@ -146,18 +151,24 @@ fn send_syncify(wc: &mut Option<WhereClause>, bounded_ty: Type) { pub(crate) struct Builder<'a> { attrs: &'a [Attribute], call_levels: Option<usize>, + concretize: bool, levels: usize, parent: Option<&'a Ident>, sig: &'a Signature, struct_: Option<&'a Ident>, struct_generics: Option<&'a Generics>, trait_: Option<&'a Ident>, - vis: &'a Visibility, + vis: &'a Visibility } impl<'a> Builder<'a> { - pub fn attrs(&mut self, attrs: &'a [Attribute]) -> &mut Self { + pub fn attrs(&mut self, attrs: &'a[Attribute]) -> &mut Self { self.attrs = attrs; + if attrs.iter() + .any(is_concretize) + { + self.concretize = true; + } self } @@ -170,16 +181,17 @@ impl<'a> Builder<'a> { let mut refpredty = Vec::new(); let (mut declosured_generics, declosured_inputs, call_exprs) = - declosurefy(&self.sig.generics, &self.sig.inputs); + if self.concretize { + concretize_args(&self.sig.generics, &self.sig.inputs) + } else { + declosurefy(&self.sig.generics, &self.sig.inputs) + }; + // TODO: make concretize and declosurefy work for the same function for fa in declosured_inputs.iter() { if let FnArg::Typed(pt) = fa { let argname = (*pt.pat).clone(); - if pat_is_self(&argname) { - // A weird receiver like `Box<Self>` - is_static = false; - continue; - } + assert!(!pat_is_self(&argname)); let aty = supersuperfy(&pt.ty, self.levels); if let Type::Reference(ref tr) = aty { predexprs.push(quote!(#argname)); @@ -188,7 +200,7 @@ impl<'a> Builder<'a> { and_token: tr.and_token, lifetime: None, mutability: None, - elem: tr.elem.clone(), + elem: tr.elem.clone() }); refpredty.push(tr2); } else { @@ -198,7 +210,7 @@ impl<'a> Builder<'a> { and_token: Token![&](Span::call_site()), lifetime: None, mutability: None, - elem: Box::new(aty.clone()), + elem: Box::new(aty.clone()) }; refpredty.push(Type::Reference(tr)); }; @@ -228,7 +240,8 @@ impl<'a> Builder<'a> { let mut return_ref = false; let mut return_refmut = false; if let Type::Reference(ref tr) = &output { - if tr.lifetime.as_ref().map_or(true, |lt| lt.ident != "static") { + if tr.lifetime.as_ref().map_or(true, |lt| lt.ident != "static") + { if tr.mutability.is_none() { return_ref = true; } else { @@ -240,29 +253,35 @@ impl<'a> Builder<'a> { compile_error(self.sig.span(), "Mockall cannot mock static methods that return non-'static references. It's unclear what the return value's lifetime should be."); } - let struct_generics = self.struct_generics.cloned().unwrap_or_default(); + let struct_generics = self.struct_generics.cloned() + .unwrap_or_default(); let (type_generics, salifetimes, srlifetimes) = split_lifetimes( struct_generics.clone(), &declosured_inputs, - &ReturnType::Type(<Token![->]>::default(), Box::new(owned_output.clone())), + &ReturnType::Type(<Token![->]>::default(), + Box::new(owned_output.clone())) ); let srltg = lifetimes_to_generics(&srlifetimes); let (call_generics, malifetimes, mrlifetimes) = split_lifetimes( declosured_generics, &declosured_inputs, - &ReturnType::Type(<Token![->]>::default(), Box::new(owned_output.clone())), + &ReturnType::Type(<Token![->]>::default(), + Box::new(owned_output.clone())) ); let mrltg = lifetimes_to_generics(&mrlifetimes); let cgenerics = merge_generics(&type_generics, &call_generics); - let egenerics = merge_generics(&merge_generics(&cgenerics, &srltg), &mrltg); - let alifetimes = salifetimes - .into_iter() - .collect::<HashSet<LifetimeDef>>() + let egenerics = merge_generics( + &merge_generics(&cgenerics, &srltg), + &mrltg); + let alifetimes = salifetimes.into_iter() + .collect::<HashSet<LifetimeParam>>() .union(&malifetimes.into_iter().collect::<HashSet<_>>()) .cloned() .collect(); - let fn_params = egenerics.type_params().map(|tp| tp.ident.clone()).collect(); + let fn_params = egenerics.type_params() + .map(|tp| tp.ident.clone()) + .collect(); let call_levels = self.call_levels.unwrap_or(self.levels); MockFunction { @@ -273,14 +292,12 @@ impl<'a> Builder<'a> { call_exprs, call_generics, call_vis: expectation_visibility(self.vis, call_levels), + concretize: self.concretize, egenerics, cgenerics, fn_params, is_static, - mod_ident: self - .parent - .unwrap_or(&Ident::new("FIXME", Span::call_site())) - .clone(), + mod_ident: self.parent.unwrap_or(&Ident::new("FIXME", Span::call_site())).clone(), output, owned_output, boxed, @@ -294,7 +311,7 @@ impl<'a> Builder<'a> { struct_generics, trait_: self.trait_.cloned(), type_generics, - privmod_vis: expectation_visibility(self.vis, self.levels), + privmod_vis: expectation_visibility(self.vis, self.levels) } } @@ -319,6 +336,7 @@ impl<'a> Builder<'a> { pub fn new(sig: &'a Signature, vis: &'a Visibility) -> Self { Builder { attrs: &[], + concretize: false, levels: 0, call_levels: None, parent: None, @@ -326,7 +344,7 @@ impl<'a> Builder<'a> { struct_: None, struct_generics: None, trait_: None, - vis, + vis } } @@ -338,7 +356,7 @@ impl<'a> Builder<'a> { /// Supply the name of the parent struct, if any pub fn struct_(&mut self, ident: &'a Ident) -> &mut Self { - self.struct_ = Some(ident); + self.struct_= Some(ident); self } @@ -359,7 +377,7 @@ impl<'a> Builder<'a> { pub(crate) struct MockFunction { /// Lifetimes of the mocked method that relate to the arguments but not the /// return value - alifetimes: Punctuated<LifetimeDef, token::Comma>, + alifetimes: Punctuated<LifetimeParam, token::Comma>, /// Names of the method arguments argnames: Vec<Pat>, /// Types of the method arguments @@ -372,6 +390,8 @@ pub(crate) struct MockFunction { call_generics: Generics, /// Visibility of the mock function itself call_vis: Visibility, + /// Are we turning generic arguments into concrete trait objects? + concretize: bool, /// Generics of the Expectation object egenerics: Generics, /// Generics of the Common object @@ -413,7 +433,7 @@ pub(crate) struct MockFunction { /// Type generics of the mock structure type_generics: Generics, /// Visibility of the expectation and its methods - privmod_vis: Visibility, + privmod_vis: Visibility } impl MockFunction { @@ -431,8 +451,7 @@ impl MockFunction { &self.egenerics } else { &self.call_generics - } - .split_for_impl(); + }.split_for_impl(); let tbf = tg.as_turbofish(); let name = self.name(); let desc = self.desc(); @@ -453,7 +472,23 @@ impl MockFunction { }; (&self.call_vis, dead_code) }; - let substruct_obj = if let Some(trait_) = &self.trait_ { + // Add #[no_mangle] attribute to preserve the function name + // as-is, without mangling, for compatibility with C functions. + let no_mangle = if let Some(ref abi) = self.sig.abi { + if let Some(ref name) = abi.name { + if name.value().ne("Rust") { + quote!(#[no_mangle]) + } else { + quote!() + } + } else { + // This is the same as extern "C" + quote!(#[no_mangle]) + } + } else { + quote!() + }; + let substruct_obj: TokenStream = if let Some(trait_) = &self.trait_ { let ident = format_ident!("{}_expectations", trait_); quote!(#ident.) } else { @@ -478,7 +513,9 @@ impl MockFunction { // Don't add a doc string. The original is included in #attrs #(#attrs)* #dead_code + #no_mangle #vis #sig { + use ::mockall::{ViaDebug, ViaNothing}; let no_match_msg = #no_match_msg; #deref { let __mockall_guard = #outer_mod_path::EXPECTATIONS @@ -489,7 +526,7 @@ impl MockFunction { * generic parameters with UnwindSafe */ /* std::panic::catch_unwind(|| */ - __mockall_guard.#call#tbf(#(#call_exprs,)*) + __mockall_guard.#call #tbf(#(#call_exprs,)*) /*)*/ }.expect(&no_match_msg) } @@ -499,9 +536,11 @@ impl MockFunction { // Don't add a doc string. The original is included in #attrs #(#attrs)* #dead_code + #no_mangle #vis #sig { + use ::mockall::{ViaDebug, ViaNothing}; let no_match_msg = #no_match_msg; - #deref self.#substruct_obj #name.#call#tbf(#(#call_exprs,)*) + #deref self.#substruct_obj #name.#call #tbf(#(#call_exprs,)*) .expect(&no_match_msg) } @@ -511,7 +550,9 @@ impl MockFunction { /// Return this method's contribution to its parent's checkpoint method pub fn checkpoint(&self) -> impl ToTokens { - let attrs = AttrFormatter::new(&self.attrs).doc(false).format(); + let attrs = AttrFormatter::new(&self.attrs) + .doc(false) + .format(); let inner_mod_ident = self.inner_mod_ident(); if self.is_static { quote!( @@ -537,13 +578,13 @@ impl MockFunction { // Supplying modname is an unfortunately hack. Ideally MockFunction // wouldn't need to know that. pub fn context_fn(&self, modname: Option<&Ident>) -> impl ToTokens { - let attrs = AttrFormatter::new(&self.attrs).doc(false).format(); - let context_docstr = format!( - "Create a [`Context`]({}{}/struct.Context.html) for mocking the `{}` method", - modname.map(|m| format!("{}/", m)).unwrap_or_default(), + let attrs = AttrFormatter::new(&self.attrs) + .doc(false) + .format(); + let context_docstr = format!("Create a [`Context`]({}{}/struct.Context.html) for mocking the `{}` method", + modname.map(|m| format!("{m}/")).unwrap_or_default(), self.inner_mod_ident(), - self.name() - ); + self.name()); let context_ident = format_ident!("{}_context", self.name()); let (_, tg, _) = self.type_generics.split_for_impl(); let outer_mod_path = self.outer_mod_path(modname); @@ -567,8 +608,8 @@ impl MockFunction { format!("{}::{}", self.mod_ident, self.sig.ident) }; let fields = vec!["{:?}"; argnames.len()].join(", "); - let fstr = format!("{}({})", name, fields); - quote!(std::format!(#fstr, #(::mockall::MaybeDebugger(&#argnames)),*)) + let fstr = format!("{name}({fields})"); + quote!(std::format!(#fstr, #((&&::mockall::ArgPrinter(&#argnames)).debug_string()),*)) } /// Generate code for the expect_ method @@ -581,18 +622,21 @@ impl MockFunction { /// trait impl. e.g. The `T` in `impl Foo for Bar<T>`. // Supplying modname is an unfortunately hack. Ideally MockFunction // wouldn't need to know that. - pub fn expect(&self, modname: &Ident, self_args: Option<&PathArguments>) -> impl ToTokens { - let attrs = AttrFormatter::new(&self.attrs).doc(false).format(); + pub fn expect(&self, modname: &Ident, self_args: Option<&PathArguments>) + -> impl ToTokens + { + let attrs = AttrFormatter::new(&self.attrs) + .doc(false) + .format(); let name = self.name(); - let expect_ident = format_ident!("expect_{}", &name); + let expect_ident = format_ident!("expect_{}", name); let expectation_obj = self.expectation_obj(self_args); let funcname = &self.sig.ident; let (_, tg, _) = if self.is_method_generic() { &self.egenerics } else { &self.call_generics - } - .split_for_impl(); + }.split_for_impl(); let (ig, _, wc) = self.call_generics.split_for_impl(); let mut wc = wc.cloned(); if self.is_method_generic() && (self.return_ref || self.return_refmut) { @@ -605,23 +649,19 @@ impl MockFunction { #[cfg(not(feature = "nightly_derive"))] let must_use = quote!(#[must_use = - "Must set return value when not using the \"nightly\" feature" - ]); + "Must set return value when not using the \"nightly\" feature" + ]); #[cfg(feature = "nightly_derive")] let must_use = quote!(); let substruct_obj = if let Some(trait_) = &self.trait_ { - let ident = format_ident!("{}_expectations", trait_); + let ident = format_ident!("{trait_}_expectations"); quote!(#ident.) } else { quote!() }; - let docstr = format!( - "Create an [`Expectation`]({}/{}/struct.Expectation.html) for mocking the `{}` method", - modname, - self.inner_mod_ident(), - funcname - ); + let docstr = format!("Create an [`Expectation`]({}/{}/struct.Expectation.html) for mocking the `{}` method", + modname, self.inner_mod_ident(), funcname); quote!( #must_use #[doc = #docstr] @@ -630,13 +670,15 @@ impl MockFunction { -> &mut #modname::#expectation_obj #wc { - self.#substruct_obj #name.expect#tbf() + self.#substruct_obj #name.expect #tbf() } ) } /// Return the name of this function's expecation object - fn expectation_obj(&self, self_args: Option<&PathArguments>) -> impl ToTokens { + fn expectation_obj(&self, self_args: Option<&PathArguments>) + -> impl ToTokens + { let inner_mod_ident = self.inner_mod_ident(); if let Some(PathArguments::AngleBracketed(abga)) = self_args { // staticize any lifetimes that might be present in the Expectation @@ -648,10 +690,8 @@ impl MockFunction { let la = GenericArgument::Lifetime(lt); abga2.args.insert(0, la); } - assert!( - !self.is_method_generic(), - "specific impls with generic methods are TODO" - ); + assert!(!self.is_method_generic(), + "specific impls with generic methods are TODO"); quote!(#inner_mod_ident::Expectation #abga2) } else { // staticize any lifetimes. This is necessary for methods that @@ -675,7 +715,9 @@ impl MockFunction { pub fn field_definition(&self, modname: Option<&Ident>) -> TokenStream { let name = self.name(); - let attrs = AttrFormatter::new(&self.attrs).doc(false).format(); + let attrs = AttrFormatter::new(&self.attrs) + .doc(false) + .format(); let expectations_obj = &self.expectations_obj(); if self.is_method_generic() { quote!(#(#attrs)* #name: #modname::#expectations_obj) @@ -702,44 +744,37 @@ impl MockFunction { if self.alifetimes.is_empty() { None } else { + let lifetimes = lifetimes_to_generic_params(&self.alifetimes); Some(BoundLifetimes { - lifetimes: self.alifetimes.clone(), + lifetimes, lt_token: <Token![<]>::default(), gt_token: <Token![>]>::default(), - ..Default::default() + .. Default::default() }) } } fn is_expectation_generic(&self) -> bool { - self.egenerics - .params - .iter() - .any(|p| matches!(p, GenericParam::Type(_))) - || self.egenerics.where_clause.is_some() + self.egenerics.params.iter().any(|p| { + matches!(p, GenericParam::Type(_)) + }) || self.egenerics.where_clause.is_some() } /// Is the mock method generic (as opposed to a non-generic method of a /// generic mock struct)? pub fn is_method_generic(&self) -> bool { - self.call_generics - .params - .iter() - .any(|p| matches!(p, GenericParam::Type(_))) - || self.call_generics.where_clause.is_some() + self.call_generics.params.iter().any(|p| { + matches!(p, GenericParam::Type(_)) + }) || self.call_generics.where_clause.is_some() } fn outer_mod_path(&self, modname: Option<&Ident>) -> Path { let mut path = if let Some(m) = modname { Path::from(PathSegment::from(m.clone())) } else { - Path { - leading_colon: None, - segments: Punctuated::new(), - } + Path { leading_colon: None, segments: Punctuated::new() } }; - path.segments - .push(PathSegment::from(self.inner_mod_ident())); + path.segments.push(PathSegment::from(self.inner_mod_ident())); path } @@ -757,48 +792,49 @@ impl MockFunction { /// Generate code for this function's private module pub fn priv_module(&self) -> impl ToTokens { - let attrs = AttrFormatter::new(&self.attrs).doc(false).format(); - let common = &Common { f: self }; - let context = &Context { f: self }; + let attrs = AttrFormatter::new(&self.attrs) + .doc(false) + .format(); + let common = &Common{f: self}; + let context = &Context{f: self}; let expectation: Box<dyn ToTokens> = if self.return_ref { - Box::new(RefExpectation { f: self }) + Box::new(RefExpectation{f: self}) } else if self.return_refmut { - Box::new(RefMutExpectation { f: self }) + Box::new(RefMutExpectation{f: self}) } else { - Box::new(StaticExpectation { f: self }) + Box::new(StaticExpectation{f: self}) }; let expectations: Box<dyn ToTokens> = if self.return_ref { - Box::new(RefExpectations { f: self }) + Box::new(RefExpectations{f: self}) } else if self.return_refmut { - Box::new(RefMutExpectations { f: self }) + Box::new(RefMutExpectations{f: self}) } else { - Box::new(StaticExpectations { f: self }) + Box::new(StaticExpectations{f: self}) }; - let generic_expectations = GenericExpectations { f: self }; + let generic_expectations = GenericExpectations{f: self}; let guard: Box<dyn ToTokens> = if self.is_expectation_generic() { - Box::new(GenericExpectationGuard { f: self }) + Box::new(GenericExpectationGuard{f: self}) } else { - Box::new(ConcreteExpectationGuard { f: self }) + Box::new(ConcreteExpectationGuard{f: self}) }; - let matcher = &Matcher { f: self }; + let matcher = &Matcher{f: self}; let std_mutexguard = if self.is_static { - quote!( - use std::sync::MutexGuard; - ) + quote!(use ::std::sync::MutexGuard;) } else { quote!() }; let inner_mod_ident = self.inner_mod_ident(); let rfunc: Box<dyn ToTokens> = if self.return_ref { - Box::new(RefRfunc { f: self }) + Box::new(RefRfunc{f: self}) } else if self.return_refmut { - Box::new(RefMutRfunc { f: self }) + Box::new(RefMutRfunc{f: self}) } else { - Box::new(StaticRfunc { f: self }) + Box::new(StaticRfunc{f: self}) }; quote!( #(#attrs)* #[allow(missing_docs)] + #[allow(clippy::too_many_arguments)] pub mod #inner_mod_ident { use super::*; use ::mockall::CaseTreeExt; @@ -825,7 +861,7 @@ impl MockFunction { /// Holds parts of the expectation that are common for all output types struct Common<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for Common<'a> { @@ -838,24 +874,35 @@ impl<'a> ToTokens for Common<'a> { let lg = lifetimes_to_generics(&self.f.alifetimes); let refpredty = &self.f.refpredty; let with_generics_idents = (0..self.f.predty.len()) - .map(|i| format_ident!("MockallMatcher{}", i)) + .map(|i| format_ident!("MockallMatcher{i}")) .collect::<Vec<_>>(); - let with_generics = with_generics_idents - .iter() + let with_generics = with_generics_idents.iter() .zip(self.f.predty.iter()) - .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, )) - .collect::<TokenStream>(); - let with_args = self - .f - .argnames - .iter() + .map(|(id, mt)| + quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ) + ).collect::<TokenStream>(); + let with_args = self.f.argnames.iter() .zip(with_generics_idents.iter()) .map(|(argname, id)| quote!(#argname: #id, )) .collect::<TokenStream>(); - let boxed_withargs = argnames - .iter() + let boxed_withargs = argnames.iter() .map(|aa| quote!(Box::new(#aa), )) .collect::<TokenStream>(); + let with_method = if self.f.concretize { + quote!( + // No `with` method when concretizing generics + ) + } else { + quote!( + fn with<#with_generics>(&mut self, #with_args) + { + let mut __mockall_guard = self.matcher.lock().unwrap(); + *__mockall_guard.deref_mut() = + Matcher::Pred(Box::new((#boxed_withargs))); + } + ) + }; + quote!( /// Holds the stuff that is independent of the output type struct Common #ig #wc { @@ -927,12 +974,7 @@ impl<'a> ToTokens for Common<'a> { self.times.times(__mockall_r) } - fn with<#with_generics>(&mut self, #with_args) - { - let mut __mockall_guard = self.matcher.lock().unwrap(); - *__mockall_guard.deref_mut() = - Matcher::Pred(Box::new((#boxed_withargs))); - } + #with_method fn withf<MockallF>(&mut self, __mockall_f: MockallF) where MockallF: #hrtb Fn(#( #refpredty, )*) @@ -992,7 +1034,7 @@ impl<'a> ToTokens for Common<'a> { /// Generates methods that are common for all Expectation types struct CommonExpectationMethods<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for CommonExpectationMethods<'a> { @@ -1002,21 +1044,36 @@ impl<'a> ToTokens for CommonExpectationMethods<'a> { let lg = lifetimes_to_generics(&self.f.alifetimes); let predty = &self.f.predty; let with_generics_idents = (0..self.f.predty.len()) - .map(|i| format_ident!("MockallMatcher{}", i)) + .map(|i| format_ident!("MockallMatcher{i}")) .collect::<Vec<_>>(); - let with_generics = with_generics_idents - .iter() + let with_generics = with_generics_idents.iter() .zip(self.f.predty.iter()) - .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, )) - .collect::<TokenStream>(); - let with_args = self - .f - .argnames - .iter() + .map(|(id, mt)| + quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ) + ).collect::<TokenStream>(); + let with_args = self.f.argnames.iter() .zip(with_generics_idents.iter()) .map(|(argname, id)| quote!(#argname: #id, )) .collect::<TokenStream>(); let v = &self.f.privmod_vis; + let with_method = if self.f.concretize { + quote!( + // No `with` method when concretizing generics + ) + } else { + quote!( + /// Set matching criteria for this Expectation. + /// + /// The matching predicate can be anything implemening the + /// [`Predicate`](../../../mockall/trait.Predicate.html) trait. Only + /// one matcher can be set per `Expectation` at a time. + #v fn with<#with_generics>(&mut self, #with_args) -> &mut Self + { + self.common.with(#(#argnames, )*); + self + } + ) + }; quote!( /// Add this expectation to a /// [`Sequence`](../../../mockall/struct.Sequence.html). @@ -1072,16 +1129,7 @@ impl<'a> ToTokens for CommonExpectationMethods<'a> { self } - /// Set matching crieteria for this Expectation. - /// - /// The matching predicate can be anything implemening the - /// [`Predicate`](../../../mockall/trait.Predicate.html) trait. Only - /// one matcher can be set per `Expectation` at a time. - #v fn with<#with_generics>(&mut self, #with_args) -> &mut Self - { - self.common.with(#(#argnames, )*); - self - } + #with_method /// Set a matching function for this Expectation. /// @@ -1104,15 +1152,14 @@ impl<'a> ToTokens for CommonExpectationMethods<'a> { self.common.withf_st(__mockall_f); self } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// Holds the moethods of the Expectations object that are common for all /// Expectation types struct CommonExpectationsMethods<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for CommonExpectationsMethods<'a> { @@ -1151,14 +1198,13 @@ impl<'a> ToTokens for CommonExpectationsMethods<'a> { Expectations(Vec::new()) } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// The ExpectationGuard structure for static methods with no generic types struct ExpectationGuardCommonMethods<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for ExpectationGuardCommonMethods<'a> { @@ -1185,21 +1231,31 @@ impl<'a> ToTokens for ExpectationGuardCommonMethods<'a> { let output = &self.f.output; let predty = &self.f.predty; let with_generics_idents = (0..self.f.predty.len()) - .map(|i| format_ident!("MockallMatcher{}", i)) + .map(|i| format_ident!("MockallMatcher{i}")) .collect::<Vec<_>>(); - let with_generics = with_generics_idents - .iter() + let with_generics = with_generics_idents.iter() .zip(self.f.predty.iter()) - .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, )) - .collect::<TokenStream>(); - let with_args = self - .f - .argnames - .iter() + .map(|(id, mt)| + quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ) + ).collect::<TokenStream>(); + let with_args = self.f.argnames.iter() .zip(with_generics_idents.iter()) .map(|(argname, id)| quote!(#argname: #id, )) .collect::<TokenStream>(); let v = &self.f.privmod_vis; + let with_method = if self.f.concretize { + quote!() + } else { + quote!( + /// Just like + /// [`Expectation::with`](struct.Expectation.html#method.with) + #v fn with<#with_generics> (&mut self, #with_args) + -> &mut Expectation #tg + { + #expectations.0[self.i].with(#(#argnames, )*) + } + ) + }; quote!( /// Just like /// [`Expectation::in_sequence`](struct.Expectation.html#method.in_sequence) @@ -1292,13 +1348,7 @@ impl<'a> ToTokens for ExpectationGuardCommonMethods<'a> { #expectations.0[self.i].times(__mockall_r) } - /// Just like - /// [`Expectation::with`](struct.Expectation.html#method.with) - #v fn with<#with_generics> (&mut self, #with_args) - -> &mut Expectation #tg - { - #expectations.0[self.i].with(#(#argnames, )*) - } + #with_method /// Just like /// [`Expectation::withf`](struct.Expectation.html#method.withf) @@ -1319,14 +1369,13 @@ impl<'a> ToTokens for ExpectationGuardCommonMethods<'a> { { #expectations.0[self.i].withf_st(__mockall_f) } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// The ExpectationGuard structure for static methods with no generic types struct ConcreteExpectationGuard<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for ConcreteExpectationGuard<'a> { @@ -1335,9 +1384,11 @@ impl<'a> ToTokens for ConcreteExpectationGuard<'a> { return; } - let common_methods = ExpectationGuardCommonMethods { f: self.f }; + let common_methods = ExpectationGuardCommonMethods{f: self.f}; let (_, tg, _) = self.f.egenerics.split_for_impl(); - let ltdef = LifetimeDef::new(Lifetime::new("'__mockall_lt", Span::call_site())); + let ltdef = LifetimeParam::new( + Lifetime::new("'__mockall_lt", Span::call_site()) + ); let mut e_generics = self.f.egenerics.clone(); e_generics.lt_token.get_or_insert(<Token![<]>::default()); e_generics.params.push(GenericParam::Lifetime(ltdef)); @@ -1385,14 +1436,13 @@ impl<'a> ToTokens for ConcreteExpectationGuard<'a> { #common_methods } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// The ExpectationGuard structure for static methods with generic types struct GenericExpectationGuard<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for GenericExpectationGuard<'a> { @@ -1401,10 +1451,12 @@ impl<'a> ToTokens for GenericExpectationGuard<'a> { return; } - let common_methods = ExpectationGuardCommonMethods { f: self.f }; + let common_methods = ExpectationGuardCommonMethods{f: self.f}; let (_, tg, _) = self.f.egenerics.split_for_impl(); let keyid = gen_keyid(&self.f.egenerics); - let ltdef = LifetimeDef::new(Lifetime::new("'__mockall_lt", Span::call_site())); + let ltdef = LifetimeParam::new( + Lifetime::new("'__mockall_lt", Span::call_site()) + ); let mut egenerics = self.f.egenerics.clone(); egenerics.lt_token.get_or_insert(<Token![<]>::default()); egenerics.params.push(GenericParam::Lifetime(ltdef)); @@ -1452,15 +1504,14 @@ impl<'a> ToTokens for GenericExpectationGuard<'a> { #common_methods } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// Generates Context, which manages the context for expectations of static /// methods. struct Context<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for Context<'a> { @@ -1469,7 +1520,9 @@ impl<'a> ToTokens for Context<'a> { return; } - let ltdef = LifetimeDef::new(Lifetime::new("'__mockall_lt", Span::call_site())); + let ltdef = LifetimeParam::new( + Lifetime::new("'__mockall_lt", Span::call_site()) + ); let mut egenerics = self.f.egenerics.clone(); egenerics.lt_token.get_or_insert(<Token![<]>::default()); egenerics.params.push(GenericParam::Lifetime(ltdef)); @@ -1477,21 +1530,20 @@ impl<'a> ToTokens for Context<'a> { let (_, e_tg, _) = egenerics.split_for_impl(); let (ty_ig, ty_tg, ty_wc) = self.f.type_generics.split_for_impl(); let mut meth_generics = self.f.call_generics.clone(); - let ltdef = LifetimeDef::new(Lifetime::new("'__mockall_lt", Span::call_site())); + let ltdef = LifetimeParam::new( + Lifetime::new("'__mockall_lt", Span::call_site()) + ); meth_generics.params.push(GenericParam::Lifetime(ltdef)); let (meth_ig, _meth_tg, meth_wc) = meth_generics.split_for_impl(); - let ctx_fn_params = self - .f - .struct_generics - .type_params() + let ctx_fn_params = self.f.struct_generics.type_params() .map(|tp| tp.ident.clone()) - .collect::<Punctuated<Ident, Token![,]>>(); + .collect::<Punctuated::<Ident, Token![,]>>(); let v = &self.f.privmod_vis; #[cfg(not(feature = "nightly_derive"))] let must_use = quote!(#[must_use = - "Must set return value when not using the \"nightly\" feature" - ]); + "Must set return value when not using the \"nightly\" feature" + ]); #[cfg(feature = "nightly_derive")] let must_use = quote!(); @@ -1541,52 +1593,76 @@ impl<'a> ToTokens for Context<'a> { } impl #ty_ig Drop for Context #ty_tg #ty_wc { fn drop(&mut self) { - Self::do_checkpoint() + if ::std::thread::panicking() { + // Drain all expectations so other tests can run with a + // blank slate. But ignore errors so we don't + // double-panic. + let _ = EXPECTATIONS + .lock() + .map(|mut g| g.checkpoint().collect::<Vec<_>>()); + } else { + // Verify expectations are satisfied + Self::do_checkpoint(); + } } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } struct Matcher<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for Matcher<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let (ig, tg, wc) = self.f.cgenerics.split_for_impl(); let argnames = &self.f.argnames; - let braces = argnames.iter().fold(String::new(), |mut acc, _argname| { - if acc.is_empty() { - acc.push_str("{}"); - } else { - acc.push_str(", {}"); - } - acc - }); + let braces = argnames.iter() + .fold(String::new(), |mut acc, _argname| { + if acc.is_empty() { + acc.push_str("{}"); + } else { + acc.push_str(", {}"); + } + acc + }); let fn_params = &self.f.fn_params; let hrtb = self.f.hrtb(); let indices = (0..argnames.len()) - .map(|i| syn::Index::from(i)) - .collect::<Vec<_>>(); + .map(|i| { + syn::Index::from(i) + }).collect::<Vec<_>>(); let lg = lifetimes_to_generics(&self.f.alifetimes); - let pred_matches = argnames - .iter() - .enumerate() + let pred_matches = argnames.iter().enumerate() .map(|(i, argname)| { let idx = syn::Index::from(i); quote!(__mockall_pred.#idx.eval(#argname),) - }) - .collect::<TokenStream>(); - let preds = self - .f - .predty - .iter() + }).collect::<TokenStream>(); + let preds = if self.f.concretize { + quote!(()) + } else { + self.f.predty.iter() .map(|t| quote!(Box<dyn #hrtb ::mockall::Predicate<#t> + Send>,)) - .collect::<TokenStream>(); + .collect::<TokenStream>() + }; let predty = &self.f.predty; let refpredty = &self.f.refpredty; + let predmatches_body = if self.f.concretize { + quote!() + } else { + quote!(Matcher::Pred(__mockall_pred) => [#pred_matches].iter().all(|__mockall_x| *__mockall_x),) + }; + let preddbg_body = if self.f.concretize { + quote!() + } else { + quote!( + Matcher::Pred(__mockall_p) => { + write!(__mockall_fmt, #braces, + #(__mockall_p.#indices,)*) + } + ) + }; quote!( enum Matcher #ig #wc { Always, @@ -1608,10 +1684,7 @@ impl<'a> ToTokens for Matcher<'a> { __mockall_f(#(#argnames, )*), Matcher::FuncSt(__mockall_f) => (__mockall_f.get())(#(#argnames, )*), - Matcher::Pred(__mockall_pred) => - [#pred_matches] - .iter() - .all(|__mockall_x| *__mockall_x), + #predmatches_body _ => unreachable!() } } @@ -1632,21 +1705,17 @@ impl<'a> ToTokens for Matcher<'a> { Matcher::Always => write!(__mockall_fmt, "<anything>"), Matcher::Func(_) => write!(__mockall_fmt, "<function>"), Matcher::FuncSt(_) => write!(__mockall_fmt, "<single threaded function>"), - Matcher::Pred(__mockall_p) => { - write!(__mockall_fmt, #braces, - #(__mockall_p.#indices,)*) - } + #preddbg_body _ => unreachable!(), } } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } struct RefRfunc<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for RefRfunc<'a> { @@ -1657,9 +1726,11 @@ impl<'a> ToTokens for RefRfunc<'a> { let owned_output = &self.f.owned_output; #[cfg(not(feature = "nightly_derive"))] - let default_err_msg = "Returning default values requires the \"nightly\" feature"; + let default_err_msg = + "Returning default values requires the \"nightly\" feature"; #[cfg(feature = "nightly_derive")] - let default_err_msg = "Can only return default values for types that impl std::Default"; + let default_err_msg = + "Can only return default values for types that impl std::Default"; quote!( enum Rfunc #ig #wc { @@ -1698,13 +1769,12 @@ impl<'a> ToTokens for RefRfunc<'a> { ::maybe_return_default()) } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } struct RefMutRfunc<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for RefMutRfunc<'a> { @@ -1718,9 +1788,11 @@ impl<'a> ToTokens for RefMutRfunc<'a> { let output = &self.f.output; #[cfg(not(feature = "nightly_derive"))] - let default_err_msg = "Returning default values requires the \"nightly\" feature"; + let default_err_msg = + "Returning default values requires the \"nightly\" feature"; #[cfg(feature = "nightly_derive")] - let default_err_msg = "Can only return default values for types that impl std::Default"; + let default_err_msg = + "Can only return default values for types that impl std::Default"; quote!( #[allow(clippy::unused_unit)] @@ -1787,13 +1859,12 @@ impl<'a> ToTokens for RefMutRfunc<'a> { ::maybe_return_default()) } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } struct StaticRfunc<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for StaticRfunc<'a> { @@ -1880,14 +1951,14 @@ impl<'a> ToTokens for StaticRfunc<'a> { /// An expectation type for functions that take a &self and return a reference struct RefExpectation<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for RefExpectation<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let argnames = &self.f.argnames; let argty = &self.f.argty; - let common_methods = CommonExpectationMethods { f: self.f }; + let common_methods = CommonExpectationMethods{f: self.f}; let desc = self.f.desc(); let funcname = self.f.funcname(); let (ig, tg, wc) = self.f.egenerics.split_for_impl(); @@ -1911,6 +1982,7 @@ impl<'a> ToTokens for RefExpectation<'a> { /// Call this [`Expectation`] as if it were the real method. #v fn call #lg (&self, #(#argnames: #argty, )*) -> #output { + use ::mockall::{ViaDebug, ViaNothing}; self.common.call(&#desc); self.rfunc.call().unwrap_or_else(|m| { let desc = std::format!( @@ -1939,19 +2011,18 @@ impl<'a> ToTokens for RefExpectation<'a> { } } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// For methods that take &mut self and return a reference struct RefMutExpectation<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for RefMutExpectation<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let common_methods = CommonExpectationMethods { f: self.f }; + let common_methods = CommonExpectationMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let desc = self.f.desc(); @@ -1976,6 +2047,7 @@ impl<'a> ToTokens for RefMutExpectation<'a> { #v fn call_mut #lg (&mut self, #(#argnames: #argty, )*) -> &mut #owned_output { + use ::mockall::{ViaDebug, ViaNothing}; self.common.call(&#desc); let desc = std::format!( "{}", self.common.matcher.lock().unwrap()); @@ -2027,19 +2099,18 @@ impl<'a> ToTokens for RefMutExpectation<'a> { } } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// An expectation type for functions return a `'static` value struct StaticExpectation<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for StaticExpectation<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let common_methods = CommonExpectationMethods { f: self.f }; + let common_methods = CommonExpectationMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let desc = self.f.desc(); @@ -2065,6 +2136,7 @@ impl<'a> ToTokens for StaticExpectation<'a> { #[doc(hidden)] #v fn call #lg (&self, #(#argnames: #argty, )* ) -> #output { + use ::mockall::{ViaDebug, ViaNothing}; self.common.call(&#desc); self.rfunc.lock().unwrap().call_mut(#(#argnames, )*) .unwrap_or_else(|message| { @@ -2201,19 +2273,18 @@ impl<'a> ToTokens for StaticExpectation<'a> { } } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// An collection of RefExpectation's struct RefExpectations<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for RefExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let common_methods = CommonExpectationsMethods { f: self.f }; + let common_methods = CommonExpectationsMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); @@ -2240,19 +2311,18 @@ impl<'a> ToTokens for RefExpectations<'a> { } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// An collection of RefMutExpectation's struct RefMutExpectations<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for RefMutExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let common_methods = CommonExpectationsMethods { f: self.f }; + let common_methods = CommonExpectationsMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); @@ -2280,19 +2350,18 @@ impl<'a> ToTokens for RefMutExpectations<'a> { } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// An collection of Expectation's for methods returning static values struct StaticExpectations<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for StaticExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let common_methods = CommonExpectationsMethods { f: self.f }; + let common_methods = CommonExpectationsMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); @@ -2319,25 +2388,24 @@ impl<'a> ToTokens for StaticExpectations<'a> { } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } struct GenericExpectations<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for GenericExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - if !self.f.is_expectation_generic() { + if ! self.f.is_expectation_generic() { return; } - if !self.f.is_static() && !self.f.is_method_generic() { + if ! self.f.is_static() && ! self.f.is_method_generic() { return; } - let ge = StaticGenericExpectations { f: self.f }; + let ge = StaticGenericExpectations{f: self.f}; let v = &self.f.privmod_vis; quote!( /// A collection of [`Expectation`](struct.Expectations.html) @@ -2364,15 +2432,14 @@ impl<'a> ToTokens for GenericExpectations<'a> { } } #ge - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } /// Generates methods for GenericExpectations for methods returning static /// values struct StaticGenericExpectations<'a> { - f: &'a MockFunction, + f: &'a MockFunction } impl<'a> ToTokens for StaticGenericExpectations<'a> { @@ -2391,19 +2458,15 @@ impl<'a> ToTokens for StaticGenericExpectations<'a> { let output = &self.f.output; let v = &self.f.privmod_vis; let (call, get, self_, downcast) = if self.f.return_refmut { - ( - format_ident!("call_mut"), - format_ident!("get_mut"), - quote!(&mut self), - format_ident!("downcast_mut"), - ) + (format_ident!("call_mut"), + format_ident!("get_mut"), + quote!(&mut self), + format_ident!("downcast_mut")) } else { - ( - format_ident!("call"), - format_ident!("get"), - quote!(&self), - format_ident!("downcast_ref"), - ) + (format_ident!("call"), + format_ident!("get"), + quote!(&self), + format_ident!("downcast_ref")) }; quote!( impl #ig ::mockall::AnyExpectations for Expectations #tg #any_wc {} @@ -2430,7 +2493,7 @@ impl<'a> ToTokens for StaticGenericExpectations<'a> { .expect() } } - ) - .to_tokens(tokens) + ).to_tokens(tokens) } } + diff --git a/src/mock_item.rs b/src/mock_item.rs index 109d9c2..e83a079 100644 --- a/src/mock_item.rs +++ b/src/mock_item.rs @@ -3,20 +3,24 @@ use super::*; use crate::{ mock_function::MockFunction, - mockable_item::{MockableItem, MockableModule}, + mockable_item::{MockableItem, MockableModule} }; /// A Mock item pub(crate) enum MockItem { Module(MockItemModule), - Struct(MockItemStruct), + Struct(MockItemStruct) } impl From<MockableItem> for MockItem { fn from(mockable: MockableItem) -> MockItem { match mockable { - MockableItem::Struct(s) => MockItem::Struct(MockItemStruct::from(s)), - MockableItem::Module(mod_) => MockItem::Module(MockItemModule::from(mod_)), + MockableItem::Struct(s) => MockItem::Struct( + MockItemStruct::from(s) + ), + MockableItem::Module(mod_) => MockItem::Module( + MockItemModule::from(mod_) + ) } } } @@ -25,14 +29,14 @@ impl ToTokens for MockItem { fn to_tokens(&self, tokens: &mut TokenStream) { match self { MockItem::Module(mod_) => mod_.to_tokens(tokens), - MockItem::Struct(s) => s.to_tokens(tokens), + MockItem::Struct(s) => s.to_tokens(tokens) } } } enum MockItemContent { Fn(Box<MockFunction>), - Tokens(TokenStream), + Tokens(TokenStream) } pub(crate) struct MockItemModule { @@ -40,7 +44,7 @@ pub(crate) struct MockItemModule { vis: Visibility, mock_ident: Ident, orig_ident: Option<Ident>, - content: Vec<MockItemContent>, + content: Vec<MockItemContent> } impl From<MockableModule> for MockItemModule { @@ -51,15 +55,20 @@ impl From<MockableModule> for MockItemModule { for item in mod_.content.into_iter() { let span = item.span(); match item { - Item::ExternCrate(_) | Item::Impl(_) => { + Item::ExternCrate(_) | Item::Impl(_) => + { // Ignore - } + }, Item::Static(is) => { - content.push(MockItemContent::Tokens(is.into_token_stream())); - } + content.push( + MockItemContent::Tokens(is.into_token_stream()) + ); + }, Item::Const(ic) => { - content.push(MockItemContent::Tokens(ic.into_token_stream())); - } + content.push( + MockItemContent::Tokens(ic.into_token_stream()) + ); + }, Item::Fn(f) => { let mf = mock_function::Builder::new(&f.sig, &f.vis) .attrs(&f.attrs) @@ -68,7 +77,7 @@ impl From<MockableModule> for MockItemModule { .call_levels(0) .build(); content.push(MockItemContent::Fn(Box::new(mf))); - } + }, Item::ForeignMod(ifm) => { for item in ifm.items { if let ForeignItem::Fn(mut f) = item { @@ -76,6 +85,11 @@ impl From<MockableModule> for MockItemModule { // foreign functions should be unsafe too, to // prevent "warning: unused unsafe" messages. f.sig.unsafety = Some(Token![unsafe](f.span())); + + // Set the ABI to match the ForeignMod's ABI + // for proper function linkage with external code. + f.sig.abi = Some(ifm.abi.clone()); + let mf = mock_function::Builder::new(&f.sig, &f.vis) .attrs(&f.attrs) .parent(&mock_ident) @@ -88,24 +102,30 @@ impl From<MockableModule> for MockItemModule { "Mockall does not yet support this type in this position. Please open an issue with your use case at https://github.com/asomers/mockall"); } } - } + }, Item::Mod(_) - | Item::Struct(_) - | Item::Enum(_) - | Item::Union(_) - | Item::Trait(_) => { - compile_error(span, "Mockall does not yet support deriving nested mocks"); - } + | Item::Struct(_) | Item::Enum(_) + | Item::Union(_) | Item::Trait(_) => + { + compile_error(span, + "Mockall does not yet support deriving nested mocks"); + }, Item::Type(ty) => { - content.push(MockItemContent::Tokens(ty.into_token_stream())); - } + content.push( + MockItemContent::Tokens(ty.into_token_stream()) + ); + }, Item::TraitAlias(ta) => { - content.push(MockItemContent::Tokens(ta.into_token_stream())); - } + content.push + (MockItemContent::Tokens(ta.into_token_stream()) + ); + }, Item::Use(u) => { - content.push(MockItemContent::Tokens(u.into_token_stream())); - } - _ => compile_error(span, "Unsupported item"), + content.push( + MockItemContent::Tokens(u.into_token_stream()) + ); + }, + _ => compile_error(span, "Unsupported item") } } MockItemModule { @@ -113,7 +133,7 @@ impl From<MockableModule> for MockItemModule { vis: mod_.vis, mock_ident: mod_.mock_ident, orig_ident, - content, + content } } } @@ -137,10 +157,9 @@ impl ToTokens for MockItemModule { #priv_mod #call #ctx_fn - ) - .to_tokens(&mut body); + ).to_tokens(&mut body); f.checkpoint().to_tokens(&mut cp_body); - } + }, } } @@ -148,11 +167,10 @@ impl ToTokens for MockItemModule { /// Verify that all current expectations for every function in /// this module are satisfied and clear them. pub fn checkpoint() { #cp_body } - ) - .to_tokens(&mut body); + ).to_tokens(&mut body); let docstr = { if let Some(ident) = &self.orig_ident { - let inner = format!("Mock version of the `{}` module", ident); + let inner = format!("Mock version of the `{ident}` module"); quote!( #[doc = #inner]) } else { // Typically an extern FFI block. Not really anything good we @@ -166,7 +184,6 @@ impl ToTokens for MockItemModule { #docstr #vis mod #modname { #body - }) - .to_tokens(tokens); + }).to_tokens(tokens); } } diff --git a/src/mock_item_struct.rs b/src/mock_item_struct.rs index 9bf5c13..f3846bc 100644 --- a/src/mock_item_struct.rs +++ b/src/mock_item_struct.rs @@ -4,61 +4,60 @@ use super::*; use quote::ToTokens; use std::collections::HashSet; -use crate::{mock_function::MockFunction, mock_trait::MockTrait}; +use crate::{ + mock_function::MockFunction, + mock_trait::MockTrait +}; fn phantom_default_inits(generics: &Generics) -> Vec<TokenStream> { - generics - .params - .iter() - .enumerate() - .map(|(count, _param)| { - let phident = format_ident!("_t{}", count); - quote!(#phident: ::std::marker::PhantomData) - }) - .collect() + generics.params + .iter() + .enumerate() + .map(|(count, _param)| { + let phident = format_ident!("_t{count}"); + quote!(#phident: ::std::marker::PhantomData) + }).collect() } /// Generate any PhantomData field definitions fn phantom_fields(generics: &Generics) -> Vec<TokenStream> { - generics - .params - .iter() - .enumerate() - .filter_map(|(count, param)| { - let phident = format_ident!("_t{}", count); - match param { - syn::GenericParam::Lifetime(l) => { - if !l.bounds.is_empty() { - compile_error( - l.bounds.span(), - "#automock does not yet support lifetime bounds on structs", - ); - } - let lifetime = &l.lifetime; - Some(quote!(#phident: ::std::marker::PhantomData<&#lifetime ()>)) - } - syn::GenericParam::Type(tp) => { - let ty = &tp.ident; - Some(quote!(#phident: ::std::marker::PhantomData<#ty>)) - } - syn::GenericParam::Const(_) => { - compile_error( - param.span(), - "#automock does not yet support generic constants", - ); - None + generics.params + .iter() + .enumerate() + .filter_map(|(count, param)| { + let phident = format_ident!("_t{count}"); + match param { + syn::GenericParam::Lifetime(l) => { + if !l.bounds.is_empty() { + compile_error(l.bounds.span(), + "#automock does not yet support lifetime bounds on structs"); } + let lifetime = &l.lifetime; + Some( + quote!(#phident: ::std::marker::PhantomData<&#lifetime ()>) + ) + }, + syn::GenericParam::Type(tp) => { + let ty = &tp.ident; + Some( + quote!(#phident: ::std::marker::PhantomData<#ty>) + ) + }, + syn::GenericParam::Const(_) => { + compile_error(param.span(), + "#automock does not yet support generic constants"); + None } - }) - .collect() + } + }).collect() } /// Filter out multiple copies of the same trait, even if they're implemented on -/// different types. But allow them if they have different attributes, which +/// different types. But allow them if they have different attributes, which /// probably indicates that they aren't meant to be compiled together. -fn unique_trait_iter<'a, I: Iterator<Item = &'a MockTrait>>( - i: I, -) -> impl Iterator<Item = &'a MockTrait> { +fn unique_trait_iter<'a, I: Iterator<Item = &'a MockTrait>>(i: I) + -> impl Iterator<Item = &'a MockTrait> +{ let mut hs = HashSet::<(Path, Vec<Attribute>)>::default(); i.filter(move |mt| { let impl_attrs = AttrFormatter::new(&mt.attrs) @@ -81,12 +80,12 @@ struct Methods(Vec<MockFunction>); impl Methods { /// Are all of these methods static? fn all_static(&self) -> bool { - self.0.iter().all(|meth| meth.is_static()) + self.0.iter() + .all(|meth| meth.is_static()) } fn checkpoints(&self) -> Vec<impl ToTokens> { - self.0 - .iter() + self.0.iter() .filter(|meth| !meth.is_static()) .map(|meth| meth.checkpoint()) .collect::<Vec<_>>() @@ -94,28 +93,26 @@ impl Methods { /// Return a fragment of code to initialize struct fields during default() fn default_inits(&self) -> Vec<TokenStream> { - self.0 - .iter() + self.0.iter() .filter(|meth| !meth.is_static()) .map(|meth| { let name = meth.name(); - let attrs = AttrFormatter::new(&meth.attrs).doc(false).format(); + let attrs = AttrFormatter::new(&meth.attrs) + .doc(false) + .format(); quote!(#(#attrs)* #name: Default::default()) - }) - .collect::<Vec<_>>() + }).collect::<Vec<_>>() } fn field_definitions(&self, modname: &Ident) -> Vec<TokenStream> { - self.0 - .iter() + self.0.iter() .filter(|meth| !meth.is_static()) .map(|meth| meth.field_definition(Some(modname))) .collect::<Vec<_>>() } fn priv_mods(&self) -> Vec<impl ToTokens> { - self.0 - .iter() + self.0.iter() .map(|meth| meth.priv_module()) .collect::<Vec<_>>() } @@ -194,35 +191,30 @@ impl From<MockableStruct> for MockItemStruct { let generics = mockable.generics.clone(); let struct_name = &mockable.name; let vis = mockable.vis; - let has_new = mockable.methods.iter().any(|meth| meth.sig.ident == "new") - || mockable.impls.iter().any(|impl_| { - impl_.items.iter().any(|ii| { - if let ImplItem::Method(iim) = ii { - iim.sig.ident == "new" - } else { - false - } - }) - }); - let methods = Methods( - mockable - .methods - .into_iter() - .map(|meth| { - mock_function::Builder::new(&meth.sig, &meth.vis) - .attrs(&meth.attrs) - .struct_(struct_name) - .struct_generics(&generics) - .levels(2) - .call_levels(0) - .build() - }) - .collect::<Vec<_>>(), - ); + let has_new = mockable.methods.iter() + .any(|meth| meth.sig.ident == "new") || + mockable.impls.iter() + .any(|impl_| + impl_.items.iter() + .any(|ii| if let ImplItem::Fn(iif) = ii { + iif.sig.ident == "new" + } else { + false + } + ) + ); + let methods = Methods(mockable.methods.into_iter() + .map(|meth| + mock_function::Builder::new(&meth.sig, &meth.vis) + .attrs(&meth.attrs) + .struct_(struct_name) + .struct_generics(&generics) + .levels(2) + .call_levels(0) + .build() + ).collect::<Vec<_>>()); let structname = &mockable.name; - let traits = mockable - .impls - .into_iter() + let traits = mockable.impls.into_iter() .map(|i| MockTrait::new(structname, &generics, i, &vis)) .collect(); @@ -236,36 +228,29 @@ impl From<MockableStruct> for MockItemStruct { modname, name: mockable.name, traits, - vis, + vis } } } impl ToTokens for MockItemStruct { fn to_tokens(&self, tokens: &mut TokenStream) { - let attrs = AttrFormatter::new(&self.attrs).async_trait(false).format(); + let attrs = AttrFormatter::new(&self.attrs) + .async_trait(false) + .format(); let consts = &self.consts; let debug_impl = self.debug_impl(); let struct_name = &self.name; let (ig, tg, wc) = self.generics.split_for_impl(); let modname = &self.modname; - let calls = self - .methods - .0 - .iter() + let calls = self.methods.0.iter() .map(|meth| meth.call(Some(modname))) .collect::<Vec<_>>(); - let contexts = self - .methods - .0 - .iter() + let contexts = self.methods.0.iter() .filter(|meth| meth.is_static()) .map(|meth| meth.context_fn(Some(modname))) .collect::<Vec<_>>(); - let expects = self - .methods - .0 - .iter() + let expects = self.methods.0.iter() .filter(|meth| !meth.is_static()) .map(|meth| meth.expect(modname, None)) .collect::<Vec<_>>(); @@ -273,17 +258,19 @@ impl ToTokens for MockItemStruct { let new_method = self.new_method(); let priv_mods = self.methods.priv_mods(); let substructs = unique_trait_iter(self.traits.iter()) - .map(|trait_| MockItemTraitImpl { - attrs: trait_.attrs.clone(), - generics: self.generics.clone(), - fieldname: format_ident!("{}_expectations", trait_.ss_name()), - methods: Methods(trait_.methods.clone()), - modname: format_ident!("{}_{}", &self.modname, trait_.ss_name()), - name: format_ident!("{}_{}", &self.name, trait_.ss_name()), - }) - .collect::<Vec<_>>(); - let substruct_expectations = substructs - .iter() + .map(|trait_| { + MockItemTraitImpl { + attrs: trait_.attrs.clone(), + generics: self.generics.clone(), + fieldname: format_ident!("{}_expectations", + trait_.ss_name()), + methods: Methods(trait_.methods.clone()), + modname: format_ident!("{}_{}", &self.modname, + trait_.ss_name()), + name: format_ident!("{}_{}", &self.name, trait_.ss_name()), + } + }).collect::<Vec<_>>(); + let substruct_expectations = substructs.iter() .filter(|ss| !ss.all_static()) .map(|ss| { let attrs = AttrFormatter::new(&ss.attrs) @@ -292,10 +279,8 @@ impl ToTokens for MockItemStruct { .format(); let fieldname = &ss.fieldname; quote!(#(#attrs)* self.#fieldname.checkpoint();) - }) - .collect::<Vec<_>>(); - let mut field_definitions = substructs - .iter() + }).collect::<Vec<_>>(); + let mut field_definitions = substructs.iter() .filter(|ss| !ss.all_static()) .map(|ss| { let attrs = AttrFormatter::new(&ss.attrs) @@ -305,12 +290,10 @@ impl ToTokens for MockItemStruct { let fieldname = &ss.fieldname; let tyname = &ss.name; quote!(#(#attrs)* #fieldname: #tyname #tg) - }) - .collect::<Vec<_>>(); + }).collect::<Vec<_>>(); field_definitions.extend(self.methods.field_definitions(modname)); field_definitions.extend(self.phantom_fields()); - let mut default_inits = substructs - .iter() + let mut default_inits = substructs.iter() .filter(|ss| !ss.all_static()) .map(|ss| { let attrs = AttrFormatter::new(&ss.attrs) @@ -319,18 +302,15 @@ impl ToTokens for MockItemStruct { .format(); let fieldname = &ss.fieldname; quote!(#(#attrs)* #fieldname: Default::default()) - }) - .collect::<Vec<_>>(); + }).collect::<Vec<_>>(); default_inits.extend(self.methods.default_inits()); default_inits.extend(self.phantom_default_inits()); - let trait_impls = self - .traits - .iter() + let trait_impls = self.traits.iter() .map(|trait_| { - let modname = format_ident!("{}_{}", &self.modname, trait_.ss_name()); + let modname = format_ident!("{}_{}", &self.modname, + trait_.ss_name()); trait_.trait_impl(&modname) - }) - .collect::<Vec<_>>(); + }).collect::<Vec<_>>(); let vis = &self.vis; quote!( #[allow(non_snake_case)] @@ -371,8 +351,7 @@ impl ToTokens for MockItemStruct { #new_method } #(#trait_impls)* - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } @@ -450,7 +429,6 @@ impl ToTokens for MockItemTraitImpl { #(#method_checkpoints)* } } - ) - .to_tokens(tokens); + ).to_tokens(tokens); } } diff --git a/src/mock_trait.rs b/src/mock_trait.rs index eba29b6..3ecf887 100644 --- a/src/mock_trait.rs +++ b/src/mock_trait.rs @@ -1,16 +1,19 @@ // vim: tw=80 use proc_macro2::Span; -use quote::{format_ident, quote, ToTokens}; +use quote::{ToTokens, format_ident, quote}; use std::{ - collections::hash_map::DefaultHasher, - hash::{Hash, Hasher}, + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher} +}; +use syn::{ + *, + spanned::Spanned }; -use syn::{spanned::Spanned, *}; use crate::{ - compile_error, - mock_function::{self, MockFunction}, AttrFormatter, + mock_function::{self, MockFunction}, + compile_error }; pub(crate) struct MockTrait { @@ -26,7 +29,7 @@ pub(crate) struct MockTrait { /// structname, but might include concrete generic parameters. self_path: PathSegment, pub types: Vec<ImplItemType>, - pub unsafety: Option<Token![unsafe]>, + pub unsafety: Option<Token![unsafe]> } impl MockTrait { @@ -40,11 +43,8 @@ impl MockTrait { // multiple traits distinguished only by their path args let mut hasher = DefaultHasher::new(); path_args.hash(&mut hasher); - format_ident!( - "{}_{}", - trait_path.segments.last().unwrap().ident, - hasher.finish() - ) + format_ident!("{}_{}", trait_path.segments.last().unwrap().ident, + hasher.finish()) } } @@ -59,12 +59,11 @@ impl MockTrait { /// * `struct_generics` - Generics of the parent structure /// * `impl_` - Mockable ItemImpl for a trait /// * `vis` - Visibility of the struct - pub fn new( - structname: &Ident, - struct_generics: &Generics, - impl_: ItemImpl, - vis: &Visibility, - ) -> Self { + pub fn new(structname: &Ident, + struct_generics: &Generics, + impl_: ItemImpl, + vis: &Visibility) -> Self + { let mut consts = Vec::new(); let mut methods = Vec::new(); let mut types = Vec::new(); @@ -76,12 +75,11 @@ impl MockTrait { }; let ss_name = MockTrait::ss_name_priv(&trait_path); let self_path = match *impl_.self_ty { - Type::Path(mut type_path) => type_path.path.segments.pop().unwrap().into_value(), + Type::Path(mut type_path) => + type_path.path.segments.pop().unwrap().into_value(), x => { - compile_error( - x.span(), - "mockall_derive only supports mocking traits and structs", - ); + compile_error(x.span(), + "mockall_derive only supports mocking traits and structs"); PathSegment::from(Ident::new("", Span::call_site())) } }; @@ -90,10 +88,10 @@ impl MockTrait { match ii { ImplItem::Const(iic) => { consts.push(iic); - } - ImplItem::Method(iim) => { - let mf = mock_function::Builder::new(&iim.sig, vis) - .attrs(&iim.attrs) + }, + ImplItem::Fn(iif) => { + let mf = mock_function::Builder::new(&iif.sig, vis) + .attrs(&iif.attrs) .levels(2) .call_levels(0) .struct_(structname) @@ -101,12 +99,13 @@ impl MockTrait { .trait_(&ss_name) .build(); methods.push(mf); - } + }, ImplItem::Type(iit) => { types.push(iit); - } + }, _ => { - compile_error(ii.span(), "This impl item is not yet supported by MockAll"); + compile_error(ii.span(), + "This impl item is not yet supported by MockAll"); } } } @@ -119,7 +118,7 @@ impl MockTrait { trait_path, self_path, types, - unsafety: impl_.unsafety, + unsafety: impl_.unsafety } } @@ -139,20 +138,14 @@ impl MockTrait { let (ig, _tg, wc) = self.generics.split_for_impl(); let consts = &self.consts; let path_args = &self.self_path.arguments; - let calls = self - .methods - .iter() - .map(|meth| meth.call(Some(modname))) - .collect::<Vec<_>>(); - let contexts = self - .methods - .iter() + let calls = self.methods.iter() + .map(|meth| meth.call(Some(modname))) + .collect::<Vec<_>>(); + let contexts = self.methods.iter() .filter(|meth| meth.is_static()) .map(|meth| meth.context_fn(Some(modname))) .collect::<Vec<_>>(); - let expects = self - .methods - .iter() + let expects = self.methods.iter() .filter(|meth| !meth.is_static()) .map(|meth| { if meth.is_method_generic() { @@ -161,8 +154,7 @@ impl MockTrait { } else { meth.expect(modname, Some(path_args)) } - }) - .collect::<Vec<_>>(); + }).collect::<Vec<_>>(); let trait_path = &self.trait_path; let self_path = &self.self_path; let types = &self.types; diff --git a/src/mockable_item.rs b/src/mockable_item.rs index 03c894d..6e15a95 100644 --- a/src/mockable_item.rs +++ b/src/mockable_item.rs @@ -12,7 +12,7 @@ fn mockable_fn(mut item_fn: ItemFn) -> ItemFn { fn mockable_item(item: Item) -> Item { match item { Item::Fn(item_fn) => Item::Fn(mockable_fn(item_fn)), - x => x, + x => x } } @@ -23,19 +23,19 @@ fn mockable_item(item: Item) -> Item { /// altered lifetimes. pub(crate) enum MockableItem { Module(MockableModule), - Struct(MockableStruct), + Struct(MockableStruct) } impl From<(Attrs, Item)> for MockableItem { fn from((attrs, item): (Attrs, Item)) -> MockableItem { match item { - Item::Impl(item_impl) => MockableItem::Struct(MockableStruct::from(item_impl)), - Item::ForeignMod(item_foreign_mod) => { - MockableItem::Module(MockableModule::from((attrs, item_foreign_mod))) - } - Item::Mod(item_mod) => MockableItem::Module(MockableModule::from(item_mod)), - Item::Trait(trait_) => MockableItem::Struct(MockableStruct::from((attrs, trait_))), - _ => panic!("automock does not support this item type"), + Item::Impl(item_impl) => + MockableItem::Struct(MockableStruct::from(item_impl)), + Item::Mod(item_mod) => + MockableItem::Module(MockableModule::from(item_mod)), + Item::Trait(trait_) => + MockableItem::Struct(MockableStruct::from((attrs, trait_))), + _ => panic!("automock does not support this item type") } } } @@ -52,90 +52,7 @@ pub(crate) struct MockableModule { pub mock_ident: Ident, /// Ident of the original module, if any pub orig_ident: Option<Ident>, - pub content: Vec<Item>, -} - -impl From<(Attrs, ItemForeignMod)> for MockableModule { - fn from((attrs, foreign): (Attrs, ItemForeignMod)) -> MockableModule { - let orig_ident = None; - let mock_ident = attrs.modname.expect(concat!( - "module name is required when mocking foreign functions,", - " like `#[automock(mod mock_ffi)]`" - )); - let vis = Visibility::Public(VisPublic { - pub_token: <Token![pub]>::default(), - }); - let attrs = quote!( - #[deprecated(since = "0.9.0", note = "Using automock directly on an extern block is deprecated. Instead, wrap the extern block in a module, and automock that, like #[automock] mod ffi { extern \"C\" { fn foo ... } }")] - ); - let mut content = vec![ - // When mocking extern blocks, we pretend that they're modules, so - // we need a "use super::*;" to ensure that types can resolve - Item::Use(ItemUse { - attrs: Vec::new(), - vis: Visibility::Inherited, - use_token: token::Use::default(), - leading_colon: None, - tree: UseTree::Path(UsePath { - ident: Ident::new("super", Span::call_site()), - colon2_token: token::Colon2::default(), - tree: Box::new(UseTree::Glob(UseGlob { - star_token: token::Star::default(), - })), - }), - semi_token: token::Semi::default(), - }), - ]; - content.extend(foreign.items.into_iter().map(|foreign_item| { - match foreign_item { - ForeignItem::Fn(f) => { - let span = f.sig.span(); - let mut sig = f.sig; - - // When mocking extern blocks, we pretend that they're - // modules. So we must supersuperfy everything by one - // level. - let vis = expectation_visibility(&f.vis, 1); - - for arg in sig.inputs.iter_mut() { - if let FnArg::Typed(pt) = arg { - *pt.ty = supersuperfy(pt.ty.as_ref(), 1); - } - } - if let ReturnType::Type(_, ty) = &mut sig.output { - **ty = supersuperfy(&*ty, 1); - } - - // Foreign functions are always unsafe. Mock foreign - // functions should be unsafe too, to prevent "warning: - // unused unsafe" messages. - sig.unsafety = Some(Token![unsafe](span)); - let block = Box::new(Block { - brace_token: token::Brace::default(), - stmts: Vec::new(), - }); - - Item::Fn(ItemFn { - attrs: f.attrs, - vis, - sig, - block, - }) - } - _ => { - compile_error(foreign_item.span(), "Unsupported foreign item type"); - Item::Verbatim(TokenStream::default()) - } - } - })); - MockableModule { - attrs, - vis, - mock_ident, - orig_ident, - content, - } - } + pub content: Vec<Item> } impl From<ItemMod> for MockableModule { @@ -145,12 +62,12 @@ impl From<ItemMod> for MockableModule { let mock_ident = format_ident!("mock_{}", mod_.ident); let orig_ident = Some(mod_.ident); let content = if let Some((_, content)) = mod_.content { - content.into_iter().map(mockable_item).collect() + content.into_iter() + .map(mockable_item) + .collect() } else { - compile_error( - span, - "automock can only mock inline modules, not modules from another file", - ); + compile_error(span, + "automock can only mock inline modules, not modules from another file"); Vec::new() }; MockableModule { @@ -158,7 +75,7 @@ impl From<ItemMod> for MockableModule { vis, mock_ident, orig_ident, - content, + content } } } diff --git a/src/mockable_struct.rs b/src/mockable_struct.rs index fe28199..4f4eb52 100644 --- a/src/mockable_struct.rs +++ b/src/mockable_struct.rs @@ -11,39 +11,37 @@ fn add_lifetime_parameters(sig: &mut Signature) { has_lifetime = true; } } - if !has_lifetime { + if ! has_lifetime { let arg_ident = match *var { Pat::Wild(_) => { - compile_error(var.span(), "Mocked methods must have named arguments"); + compile_error(var.span(), + "Mocked methods must have named arguments"); format_ident!("dont_care") - } + }, Pat::Ident(ref pat_ident) => { if let Some(r) = &pat_ident.by_ref { - compile_error( - r.span(), - "Mockall does not support by-reference argument bindings", - ); + compile_error(r.span(), + "Mockall does not support by-reference argument bindings"); } if let Some((_at, subpat)) = &pat_ident.subpat { - compile_error( - subpat.span(), - "Mockall does not support subpattern bindings", - ); + compile_error(subpat.span(), + "Mockall does not support subpattern bindings"); } pat_ident.ident.clone() - } + }, _ => { - compile_error(var.span(), "Unsupported argument type"); + compile_error(var.span(), + "Unsupported argument type"); format_ident!("dont_care") } }; - let s = format!("'__mockall_{}", arg_ident); + let s = format!("'__mockall_{arg_ident}"); let span = Span::call_site(); let lt = Lifetime::new(&s, span); to.bounds.push(TypeParamBound::Lifetime(lt.clone())); generics.lt_token.get_or_insert(Token![<](span)); generics.gt_token.get_or_insert(Token![>](span)); - let gpl = GenericParam::Lifetime(LifetimeDef::new(lt)); + let gpl = GenericParam::Lifetime(LifetimeParam::new(lt)); generics.params.push(gpl); } } @@ -63,7 +61,7 @@ fn add_lifetime_parameters(sig: &mut Signature) { } else { add_to_type(generics, var, tr.elem.as_mut()); } - } + }, Type::TraitObject(to) => { add_to_trait_object(generics, var, to); // We need to wrap it in a Paren. Otherwise it won't be @@ -71,19 +69,19 @@ fn add_lifetime_parameters(sig: &mut Signature) { // due to a "ambiguous `+` in a type" error *tr.elem = Type::Paren(TypeParen { paren_token: token::Paren::default(), - elem: Box::new(Type::TraitObject(to.clone())), + elem: Box::new(Type::TraitObject(to.clone())) }); - } + }, _ => add_to_type(generics, var, tr.elem.as_mut()), } - } + }, Type::Slice(ts) => add_to_type(generics, var, ts.elem.as_mut()), Type::Tuple(tt) => { for ty in tt.elems.iter_mut() { add_to_type(generics, var, ty) } - } - _ => compile_error(ty.span(), "unsupported type in this position"), + }, + _ => compile_error(ty.span(), "unsupported type in this position") } } @@ -96,12 +94,12 @@ fn add_lifetime_parameters(sig: &mut Signature) { /// Generate a #[derive(Debug)] Attribute fn derive_debug() -> Attribute { + let ml = parse2(quote!(derive(Debug))).unwrap(); Attribute { pound_token: <Token![#]>::default(), style: AttrStyle::Outer, bracket_token: token::Bracket::default(), - path: Path::from(format_ident!("derive")), - tokens: quote!((Debug)), + meta: Meta::List(ml) } } @@ -110,29 +108,27 @@ fn mock_ident_in_type(ty: &mut Type) { match ty { Type::Path(type_path) => { if type_path.path.segments.len() != 1 { - compile_error( - type_path.path.span(), - "mockall_derive only supports structs defined in the current module", - ); + compile_error(type_path.path.span(), + "mockall_derive only supports structs defined in the current module"); return; } let ident = &mut type_path.path.segments.last_mut().unwrap().ident; *ident = gen_mock_ident(ident) - } + }, x => { - compile_error( - x.span(), - "mockall_derive only supports mocking traits and structs", - ); + compile_error(x.span(), + "mockall_derive only supports mocking traits and structs"); } }; } /// Performs transformations on the ItemImpl to make it mockable -fn mockable_item_impl(mut impl_: ItemImpl, name: &Ident, generics: &Generics) -> ItemImpl { +fn mockable_item_impl(mut impl_: ItemImpl, name: &Ident, generics: &Generics) + -> ItemImpl +{ mock_ident_in_type(&mut impl_.self_ty); for item in impl_.items.iter_mut() { - if let ImplItem::Method(ref mut iim) = item { + if let ImplItem::Fn(ref mut iim) = item { mockable_method(iim, name, generics); } } @@ -140,7 +136,8 @@ fn mockable_item_impl(mut impl_: ItemImpl, name: &Ident, generics: &Generics) -> } /// Performs transformations on the method to make it mockable -fn mockable_method(meth: &mut ImplItemMethod, name: &Ident, generics: &Generics) { +fn mockable_method(meth: &mut ImplItemFn, name: &Ident, generics: &Generics) +{ demutify(&mut meth.sig.inputs); deselfify_args(&mut meth.sig.inputs, name, generics); add_lifetime_parameters(&mut meth.sig); @@ -154,7 +151,11 @@ fn mockable_method(meth: &mut ImplItemMethod, name: &Ident, generics: &Generics) } /// Performs transformations on the method to make it mockable -fn mockable_trait_method(meth: &mut TraitItemMethod, name: &Ident, generics: &Generics) { +fn mockable_trait_method( + meth: &mut TraitItemFn, + name: &Ident, + generics: &Generics) +{ demutify(&mut meth.sig.inputs); deselfify_args(&mut meth.sig.inputs, name, generics); add_lifetime_parameters(&mut meth.sig); @@ -168,34 +169,41 @@ fn mockable_trait_method(meth: &mut TraitItemMethod, name: &Ident, generics: &Ge } /// Generates a mockable item impl from a trait method definition -fn mockable_trait(trait_: ItemTrait, name: &Ident, generics: &Generics) -> ItemImpl { - let items = trait_ - .items - .into_iter() - .map(|ti| match ti { - TraitItem::Method(mut tim) => { - mockable_trait_method(&mut tim, name, generics); - ImplItem::Method(tim2iim(tim, &Visibility::Inherited)) - } - TraitItem::Const(tic) => ImplItem::Const(tic2iic(tic, &Visibility::Inherited)), - TraitItem::Type(tit) => ImplItem::Type(tit2iit(tit, &Visibility::Inherited)), +fn mockable_trait(trait_: ItemTrait, name: &Ident, generics: &Generics) + -> ItemImpl +{ + let items = trait_.items.into_iter() + .map(|ti| { + match ti { + TraitItem::Fn(mut tif) => { + mockable_trait_method(&mut tif, name, generics); + ImplItem::Fn(tif2iif(tif, &Visibility::Inherited)) + }, + TraitItem::Const(tic) => { + ImplItem::Const(tic2iic(tic, &Visibility::Inherited)) + }, + TraitItem::Type(tit) => { + ImplItem::Type(tit2iit(tit, &Visibility::Inherited)) + }, _ => { compile_error(ti.span(), "Unsupported in this context"); ImplItem::Verbatim(TokenStream::new()) } - }) - .collect::<Vec<_>>(); + } + }).collect::<Vec<_>>(); let mut trait_path = Path::from(trait_.ident); let mut struct_path = Path::from(name.clone()); let (_, stg, _) = generics.split_for_impl(); let (_, ttg, _) = trait_.generics.split_for_impl(); if let Ok(abga) = parse2::<AngleBracketedGenericArguments>(quote!(#stg)) { - struct_path.segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(abga); + struct_path.segments.last_mut().unwrap().arguments = + PathArguments::AngleBracketed(abga); } if let Ok(abga) = parse2::<AngleBracketedGenericArguments>(quote!(#ttg)) { - trait_path.segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(abga); + trait_path.segments.last_mut().unwrap().arguments = + PathArguments::AngleBracketed(abga); } - let self_ty = Box::new(Type::Path(TypePath { + let self_ty = Box::new(Type::Path(TypePath{ qself: None, path: struct_path, })); @@ -208,7 +216,7 @@ fn mockable_trait(trait_: ItemTrait, name: &Ident, generics: &Generics) -> ItemI trait_: Some((None, trait_path, <Token![for]>::default())), self_ty, brace_token: trait_.brace_token, - items, + items } } @@ -231,10 +239,8 @@ fn sanity_check_sig(sig: &Signature) { fn tic2iic(tic: TraitItemConst, vis: &syn::Visibility) -> ImplItemConst { let span = tic.span(); let (eq_token, expr) = tic.default.unwrap_or_else(|| { - compile_error( - span, - "Mocked associated consts must have a default implementation", - ); + compile_error(span, + "Mocked associated consts must have a default implementation"); (<Token![=]>::default(), Expr::Verbatim(TokenStream::new())) }); ImplItemConst { @@ -242,27 +248,30 @@ fn tic2iic(tic: TraitItemConst, vis: &syn::Visibility) -> ImplItemConst { vis: vis.clone(), defaultness: None, const_token: tic.const_token, + generics: tic.generics, ident: tic.ident, colon_token: tic.colon_token, ty: tic.ty, eq_token, expr, - semi_token: tic.semi_token, + semi_token: tic.semi_token } } -/// Converts a TraitItemMethod into an ImplItemMethod -fn tim2iim(m: syn::TraitItemMethod, vis: &syn::Visibility) -> syn::ImplItemMethod { +/// Converts a TraitItemFn into an ImplItemFn +fn tif2iif(m: syn::TraitItemFn, vis: &syn::Visibility) + -> syn::ImplItemFn +{ let empty_block = Block { brace_token: token::Brace::default(), - stmts: Vec::new(), + stmts: Vec::new() }; - syn::ImplItemMethod { + syn::ImplItemFn{ attrs: m.attrs, vis: vis.clone(), defaultness: None, sig: m.sig, - block: empty_block, + block: empty_block } } @@ -270,7 +279,8 @@ fn tim2iim(m: syn::TraitItemMethod, vis: &syn::Visibility) -> syn::ImplItemMetho fn tit2iit(tit: TraitItemType, vis: &Visibility) -> ImplItemType { let span = tit.span(); let (eq_token, ty) = tit.default.unwrap_or_else(|| { - compile_error(span, "associated types in mock! must be fully specified"); + compile_error(span, + "associated types in mock! must be fully specified"); (token::Eq::default(), Type::Verbatim(TokenStream::new())) }); ImplItemType { @@ -286,12 +296,28 @@ fn tit2iit(tit: TraitItemType, vis: &Visibility) -> ImplItemType { } } +/// Like a TraitItemFn, but with a visibility +struct TraitItemVFn { + pub vis: Visibility, + pub tif: TraitItemFn +} + +impl Parse for TraitItemVFn { + fn parse(input: ParseStream) -> syn::parse::Result<Self> { + let attrs = input.call(Attribute::parse_outer)?; + let vis: syn::Visibility = input.parse()?; + let mut tif: TraitItemFn = input.parse()?; + tif.attrs = attrs; + Ok(Self{vis, tif}) + } +} + pub(crate) struct MockableStruct { pub attrs: Vec<Attribute>, pub consts: Vec<ImplItemConst>, pub generics: Generics, /// Inherent methods of the mockable struct - pub methods: Vec<ImplItemMethod>, + pub methods: Vec<ImplItemFn>, pub name: Ident, pub vis: Visibility, pub impls: Vec<ItemImpl>, @@ -300,24 +326,18 @@ pub(crate) struct MockableStruct { impl MockableStruct { /// Does this struct derive Debug? pub fn derives_debug(&self) -> bool { - self.attrs.iter().any(|attr| { - if let Ok(Meta::List(ml)) = attr.parse_meta() { - let i = ml.path.get_ident(); - if i.map_or(false, |i| *i == "derive") { - ml.nested.iter().any(|nm| { - if let NestedMeta::Meta(m) = nm { - let i = m.path().get_ident(); - i.map_or(false, |i| *i == "Debug") - } else { - false - } - }) - } else { - false - } - } else { - false + self.attrs.iter() + .any(|attr|{ + let mut derive_debug = false; + if attr.path().is_ident("derive") { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("Debug") { + derive_debug = true; + } + Ok(()) + }).unwrap(); } + derive_debug }) } } @@ -338,7 +358,7 @@ impl From<(Attrs, ItemTrait)> for MockableStruct { name, generics, methods: Vec::new(), - impls, + impls } } } @@ -349,12 +369,10 @@ impl From<ItemImpl> for MockableStruct { Type::Path(type_path) => { let n = find_ident_from_path(&type_path.path).0; gen_mock_ident(&n) - } + }, x => { - compile_error( - x.span(), - "mockall_derive only supports mocking traits and structs", - ); + compile_error(x.span(), + "mockall_derive only supports mocking traits and structs"); Ident::new("", Span::call_site()) } }; @@ -363,8 +381,7 @@ impl From<ItemImpl> for MockableStruct { let mut consts = Vec::new(); let generics = item_impl.generics.clone(); let mut methods = Vec::new(); - let pub_token = Token![pub](Span::call_site()); - let vis = Visibility::Public(VisPublic { pub_token }); + let vis = Visibility::Public(Token![pub](Span::call_site())); let mut impls = Vec::new(); if let Some((bang, _path, _)) = &item_impl.trait_ { if bang.is_some() { @@ -377,12 +394,14 @@ impl From<ItemImpl> for MockableStruct { let mut attrs = Attrs::default(); for item in item_impl.items.iter() { match item { - ImplItem::Const(_iic) => (), - ImplItem::Method(_meth) => (), + ImplItem::Const(_iic) => + (), + ImplItem::Fn(_meth) => + (), ImplItem::Type(ty) => { attrs.attrs.insert(ty.ident.clone(), ty.ty.clone()); - } - x => compile_error(x.span(), "Unsupported by automock"), + }, + x => compile_error(x.span(), "Unsupported by automock") } } attrs.substitute_item_impl(&mut item_impl); @@ -390,13 +409,14 @@ impl From<ItemImpl> for MockableStruct { } else { for item in item_impl.items.into_iter() { match item { - ImplItem::Method(mut meth) => { + ImplItem::Fn(mut meth) => { mockable_method(&mut meth, &name, &item_impl.generics); methods.push(meth) - } + }, ImplItem::Const(iic) => consts.push(iic), // Rust doesn't allow types in an inherent impl - x => compile_error(x.span(), "Unsupported by Mockall in this context"), + x => compile_error(x.span(), + "Unsupported by Mockall in this context"), } } }; @@ -428,7 +448,9 @@ impl Parse for MockableStruct { while !impl_content.is_empty() { let item: ImplItem = impl_content.parse()?; match item { - ImplItem::Method(mut iim) => { + ImplItem::Verbatim(ts) => { + let tivf: TraitItemVFn = parse2(ts)?; + let mut iim = tif2iif(tivf.tif, &tivf.vis); mockable_method(&mut iim, &name, &generics); methods.push(iim); } @@ -443,32 +465,33 @@ impl Parse for MockableStruct { while !input.is_empty() { let item: Item = input.parse()?; match item { - Item::Trait(it) => { - let note = "Deprecated mock! syntax. Instead of \"trait X\", write \"impl X for Y\". See PR #205"; - let mut impl_ = mockable_trait(it, &name, &generics); - impl_.attrs.push(Attribute { - pound_token: <token::Pound>::default(), - style: AttrStyle::Outer, - bracket_token: token::Bracket::default(), - path: Path::from(format_ident!("deprecated")), - tokens: quote!((since = "0.9.0", note = #note)), - }); - impls.push(impl_) + Item::Impl(mut ii) => { + for item in ii.items.iter_mut() { + // Convert any methods that syn couldn't parse as + // ImplItemFn. + if let ImplItem::Verbatim(ts) = item { + let tif: TraitItemFn = parse2(ts.clone()).unwrap(); + let iim = tif2iif(tif, &Visibility::Inherited); + *item = ImplItem::Fn(iim); + } + } + impls.push(mockable_item_impl(ii, &name, &generics)); } - Item::Impl(ii) => impls.push(mockable_item_impl(ii, &name, &generics)), _ => return Err(input.error("Unsupported in this context")), } } - Ok(MockableStruct { - attrs, - consts, - generics, - methods, - name, - vis, - impls, - }) + Ok( + MockableStruct { + attrs, + consts, + generics, + methods, + name, + vis, + impls + } + ) } } @@ -476,167 +499,134 @@ impl Parse for MockableStruct { mod t { use super::*; - mod add_lifetime_parameters { - use super::*; - - #[test] - fn array() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo(&self, x: [&dyn T; 1]); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo<'__mockall_x>(&self, x: [&(dyn T + '__mockall_x); 1]); - ) - .to_string(), - quote!(#meth).to_string() - ); - } +mod add_lifetime_parameters { + use super::*; - #[test] - fn bare_fn_with_named_args() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo(&self, x: fn(&dyn T)); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo(&self, x: fn(&dyn T)); - ) + #[test] + fn array() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo(&self, x: [&dyn T; 1]); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo<'__mockall_x>(&self, x: [&(dyn T + '__mockall_x); 1]);) .to_string(), - quote!(#meth).to_string() - ); - } + quote!(#meth).to_string() + ); + } - #[test] - fn plain() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo(&self, x: &dyn T); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x)); - ) - .to_string(), - quote!(#meth).to_string() - ); - } + #[test] + fn bare_fn_with_named_args() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo(&self, x: fn(&dyn T)); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo(&self, x: fn(&dyn T));).to_string(), + quote!(#meth).to_string() + ); + } - #[test] - fn slice() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo(&self, x: &[&dyn T]); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo<'__mockall_x>(&self, x: &[&(dyn T + '__mockall_x)]); - ) + #[test] + fn plain() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo(&self, x: &dyn T); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x));) .to_string(), - quote!(#meth).to_string() - ); - } + quote!(#meth).to_string() + ); + } - #[test] - fn tuple() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo(&self, x: (&dyn T, u32)); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo<'__mockall_x>(&self, x: (&(dyn T + '__mockall_x), u32)); - ) + #[test] + fn slice() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo(&self, x: &[&dyn T]); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo<'__mockall_x>(&self, x: &[&(dyn T + '__mockall_x)]);) .to_string(), - quote!(#meth).to_string() - ); - } + quote!(#meth).to_string() + ); + } - #[test] - fn with_anonymous_lifetime() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo(&self, x: &(dyn T + '_)); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo(&self, x: &(dyn T + '_)); - ) + #[test] + fn tuple() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo(&self, x: (&dyn T, u32)); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo<'__mockall_x>(&self, x: (&(dyn T + '__mockall_x), u32));) .to_string(), - quote!(#meth).to_string() - ); - } + quote!(#meth).to_string() + ); + } - #[test] - fn with_parens() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo(&self, x: &(dyn T)); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x)); - ) - .to_string(), - quote!(#meth).to_string() - ); - } + #[test] + fn with_anonymous_lifetime() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo(&self, x: &(dyn T + '_)); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo(&self, x: &(dyn T + '_));).to_string(), + quote!(#meth).to_string() + ); + } - #[test] - fn with_lifetime_parameter() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo<'a>(&self, x: &(dyn T + 'a)); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo<'a>(&self, x: &(dyn T + 'a)); - ) + #[test] + fn with_parens() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo(&self, x: &(dyn T)); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x));) .to_string(), - quote!(#meth).to_string() - ); - } + quote!(#meth).to_string() + ); + } - #[test] - fn with_static_lifetime() { - let mut meth: TraitItemMethod = parse2(quote!( - fn foo(&self, x: &(dyn T + 'static)); - )) - .unwrap(); - add_lifetime_parameters(&mut meth.sig); - assert_eq!( - quote!( - fn foo(&self, x: &(dyn T + 'static)); - ) - .to_string(), - quote!(#meth).to_string() - ); - } + #[test] + fn with_lifetime_parameter() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo<'a>(&self, x: &(dyn T + 'a)); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo<'a>(&self, x: &(dyn T + 'a));).to_string(), + quote!(#meth).to_string() + ); } - mod sanity_check_sig { - use super::*; - - #[test] - #[should_panic( - expected = "Mockall does not support \"impl trait\" in argument position. Use \"T: SomeTrait\" instead." - )] - fn impl_trait() { - let meth: ImplItemMethod = parse2(quote!( - fn foo(&self, x: impl SomeTrait); - )) - .unwrap(); - sanity_check_sig(&meth.sig); - } + #[test] + fn with_static_lifetime() { + let mut meth: TraitItemFn = parse2(quote!( + fn foo(&self, x: &(dyn T + 'static)); + )).unwrap(); + add_lifetime_parameters(&mut meth.sig); + assert_eq!( + quote!(fn foo(&self, x: &(dyn T + 'static));).to_string(), + quote!(#meth).to_string() + ); } + +} + +mod sanity_check_sig { + use super::*; + + #[test] + #[should_panic(expected = "Mockall does not support \"impl trait\" in argument position. Use \"T: SomeTrait\" instead.")] + fn impl_trait() { + let meth: ImplItemFn = parse2(quote!( + fn foo(&self, x: impl SomeTrait) {} + )).unwrap(); + sanity_check_sig(&meth.sig); + } +} } |