summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Geisler <mgeisler@google.com>2024-04-25 15:25:11 +0200
committerMartin Geisler <mgeisler@google.com>2024-04-26 11:20:15 +0200
commit6b1e56537b60434209b70589bc290e650d03a316 (patch)
treeb6123aa56bfe54ad95cd008e97560f7321fc53d0
parent84c51004aa5d80f77a6f175fbcf18872aff3480f (diff)
downloadmockall_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.json2
-rw-r--r--Android.bp16
-rw-r--r--Cargo.toml10
-rw-r--r--METADATA22
-rw-r--r--cargo_embargo.json4
-rw-r--r--patches/use-deprecated-syn1-dep.patch21
-rw-r--r--src/automock.rs180
-rw-r--r--src/lib.rs389
-rw-r--r--src/mock_function.rs665
-rw-r--r--src/mock_item.rs91
-rw-r--r--src/mock_item_struct.rs234
-rw-r--r--src/mock_trait.rs80
-rw-r--r--src/mockable_item.rs115
-rw-r--r--src/mockable_struct.rs522
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
diff --git a/Android.bp b/Android.bp
index 59f7665..fc6030d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
],
}
diff --git a/Cargo.toml b/Cargo.toml
index 89d8bc1..4935150 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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",
diff --git a/METADATA b/METADATA
index eaa868e..046e0b0 100644
--- a/METADATA
+++ b/METADATA
@@ -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));
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 6308c45..aae5361 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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);
+ }
+}
}