Skip to content

Commit

Permalink
Fix mocking generic methods that use Self
Browse files Browse the repository at this point in the history
Now it's possible to automock generic constructor methods

Fixes #23
  • Loading branch information
asomers committed Aug 10, 2019
1 parent 3ae8d97 commit a0188fd
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 28 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Fixed mocking structs and traits with more than one static method.
([#22](https://github.com/asomers/mockall/pull/22))

- Methods of generics structs and traits that reference `Self` (such as
- Methods of generic structs and traits that reference `Self` (such as
constructors or comparators) can now be mocked without any hacks.
([#21](https://github.com/asomers/mockall/pull/21))
([#25](https://github.com/asomers/mockall/pull/25))

- Specializing methods of generic structs (and traits) can now be mocked
without the hack of duplicating the struct's (or trait's) generic parameters.
Expand Down
17 changes: 17 additions & 0 deletions mockall/tests/automock_generic_constructor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// vim: tw=80
//! A non-generic struct can have a generic constructor method

use mockall::*;

#[automock]
trait Foo {
fn build<T: 'static>(t: T) -> Self;
}

#[test]
fn returning_once() {
MockFoo::expect_build::<i16>()
.return_once(|_| MockFoo::default());

let _mock: MockFoo = MockFoo::build::<i16>(-1);
}
2 changes: 1 addition & 1 deletion mockall_derive/src/automock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ fn mock_function(vis: &Visibility,
&format!("{}_expectation", ident),
Span::call_site());
let mut out = TokenStream::new();
Expectation::new(&TokenStream::new(), &inputs, generics,
Expectation::new(&TokenStream::new(), &inputs, None, generics,
&mod_ident, None, &decl.output, &expect_vis).to_tokens(&mut out);
quote!(
::mockall::lazy_static! {
Expand Down
46 changes: 26 additions & 20 deletions mockall_derive/src/expectation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct Common<'a> {
/// The expectation's generic types as a list of types
fn_params: Punctuated<Ident, Token![,]>,
/// Generics of the Expectation object
generics: Generics,
egenerics: Generics,
/// Is this for a static method or free function?
is_static: bool,
/// Expressions that create the predicate arguments from the call arguments
Expand Down Expand Up @@ -169,7 +169,7 @@ impl<'a> Common<'a> {

/// Common methods of the Expectations structs
fn expectations_methods(&self) -> TokenStream {
let (ig, tg, wc) = self.generics.split_for_impl();
let (ig, tg, wc) = self.egenerics.split_for_impl();
let v = &self.vis;
quote!(
/// A collection of [`Expectation`](struct.Expectations.html)
Expand Down Expand Up @@ -270,7 +270,7 @@ impl<'a> Expectation<'a> {
let fn_params = &self.common().fn_params;
let predty = &self.common().predty;
let rfunc_ts = self.rfunc();
let (ig, tg, wc) = self.common().generics.split_for_impl();
let (ig, tg, wc) = self.common().egenerics.split_for_impl();
let preds = TokenStream::from_iter(
self.common().predty.iter().map(|t|
quote!(Box<::mockall::Predicate<#t> + Send>,)
Expand Down Expand Up @@ -480,16 +480,17 @@ impl<'a> Expectation<'a> {
/// `#[cfg()]`
/// * `args` - Arguments for the mock method, which may be
/// slightly different than on the original method.
/// * `generics` - Merged generics of the method and its parent
/// struct
/// * `struct_generics` - Generics of the parent struct, if any.
/// * `meth_generics` - Generics of the method being mocked
/// * `meth_ident` - Name of the original method
/// * `parent_ident` - Name of the parent struct, if any.
/// * `return_type` - Return type of the mock method
/// * `vis` - Visibility of the expectation, *already supersuperfied*.
pub(crate) fn new(
attrs: &'a TokenStream,
args: &Punctuated<FnArg, Token![,]>,
generics: &Generics,
struct_generics: Option<&'a Generics>,
meth_generics: &'a Generics,
meth_ident: &'a Ident,
parent_ident: Option<&Ident>,
return_type: &ReturnType,
Expand Down Expand Up @@ -519,6 +520,11 @@ impl<'a> Expectation<'a> {
() // Strip out the "&self" argument
}
}
let generics = if let Some(g) = struct_generics {
merge_generics(g, meth_generics)
} else {
meth_generics.clone()
};
let mut egenerics = generics.clone();
for p in egenerics.params.iter_mut() {
if let GenericParam::Type(tp) = p {
Expand All @@ -540,7 +546,7 @@ impl<'a> Expectation<'a> {
let mut rt: Type = (**ty).clone();
destrify(&mut rt);
if let Some(i) = parent_ident {
crate::deselfify(&mut rt, i, generics);
crate::deselfify(&mut rt, i, &struct_generics.unwrap());
}
if let Type::Reference(ref tr) = rt {
if tr.lifetime.as_ref()
Expand All @@ -567,7 +573,7 @@ impl<'a> Expectation<'a> {
argty,
attrs,
fn_params,
generics: egenerics,
egenerics,
is_static,
predexprs,
predty,
Expand Down Expand Up @@ -616,7 +622,7 @@ impl<'a> StaticExpectation<'a> {
fn expectation(&self, em_ts: TokenStream) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, wc) = self.common.generics.split_for_impl();
let (ig, tg, wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let predexprs = &self.common.predexprs;
let v = &self.common.vis;
Expand Down Expand Up @@ -743,7 +749,7 @@ impl<'a> StaticExpectation<'a> {
fn expectations_methods(&self) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, wc) = self.common.generics.split_for_impl();
let (ig, tg, wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let predexprs = &self.common.predexprs;
let v = &self.common.vis;
Expand Down Expand Up @@ -780,7 +786,7 @@ impl<'a> StaticExpectation<'a> {
fn generic_expectations_methods(&self) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, wc) = self.common.generics.split_for_impl();
let (ig, tg, wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let tbf = tg.as_turbofish();
let v = &self.common.vis;
Expand Down Expand Up @@ -810,7 +816,7 @@ impl<'a> StaticExpectation<'a> {
}

fn rfunc(&self) -> TokenStream {
let (ig, tg, wc) = self.common.generics.split_for_impl();
let (ig, tg, wc) = self.common.egenerics.split_for_impl();
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let fn_params = &self.common.fn_params;
Expand Down Expand Up @@ -873,14 +879,14 @@ impl<'a> StaticExpectation<'a> {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let fn_params = &self.common.fn_params;
let (_ig, tg, _wc) = self.common.generics.split_for_impl();
let (_ig, tg, _wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let predty = &self.common.predty;
let tbf = tg.as_turbofish();
let v = &self.common.vis;

// Add a lifetime parameter, needed by MutexGuard
let mut ltgenerics = self.common.generics.clone();
let mut ltgenerics = self.common.egenerics.clone();
let ltdef = LifetimeDef::new(
Lifetime::new("'__mockall_lt", Span::call_site())
);
Expand Down Expand Up @@ -1196,7 +1202,7 @@ impl<'a> RefExpectation<'a> {
fn expectation(&self, em_ts: TokenStream) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, _wc) = self.common.generics.split_for_impl();
let (ig, tg, _wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let predexprs = &self.common.predexprs;
let v = &self.common.vis;
Expand Down Expand Up @@ -1242,7 +1248,7 @@ impl<'a> RefExpectation<'a> {
fn expectations_methods(&self) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, _wc) = self.common.generics.split_for_impl();
let (ig, tg, _wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let predexprs = &self.common.predexprs;
let v = &self.common.vis;
Expand Down Expand Up @@ -1275,7 +1281,7 @@ impl<'a> RefExpectation<'a> {
fn generic_expectations_methods(&self) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, wc) = self.common.generics.split_for_impl();
let (ig, tg, wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let tbf = tg.as_turbofish();
let v = &self.common.vis;
Expand Down Expand Up @@ -1332,7 +1338,7 @@ impl<'a> RefMutExpectation<'a> {
fn expectation(&self, em_ts: TokenStream) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, _wc) = self.common.generics.split_for_impl();
let (ig, tg, _wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let predexprs = &self.common.predexprs;
let v = &self.common.vis;
Expand Down Expand Up @@ -1407,7 +1413,7 @@ impl<'a> RefMutExpectation<'a> {
fn expectations_methods(&self) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, _wc) = self.common.generics.split_for_impl();
let (ig, tg, _wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let predexprs = &self.common.predexprs;
let v = &self.common.vis;
Expand Down Expand Up @@ -1441,7 +1447,7 @@ impl<'a> RefMutExpectation<'a> {
fn generic_expectations_methods(&self) -> TokenStream {
let argnames = &self.common.argnames;
let argty = &self.common.argty;
let (ig, tg, wc) = self.common.generics.split_for_impl();
let (ig, tg, wc) = self.common.egenerics.split_for_impl();
let output = &self.common.output;
let tbf = tg.as_turbofish();
let v = &self.common.vis;
Expand Down
3 changes: 0 additions & 3 deletions mockall_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ fn deselfify(literal_type: &mut Type, actual: &Ident, generics: &Generics) {
if seg.ident == "Self" {
seg.ident = actual.clone();
if let PathArguments::None = seg.arguments {
//let (_, tg, _) = generics.split_for_impl();
if !generics.params.is_empty() {
let args = Punctuated::from_iter(
generics.params.iter().map(|gp| {
Expand Down Expand Up @@ -427,8 +426,6 @@ fn gen_mod_ident(struct_: &Ident, trait_: Option<&Ident>)
/// Combine two Generics structs, producing a new one that has the union of
/// their parameters.
fn merge_generics(x: &Generics, y: &Generics) -> Generics {
//dbg!(x);
//dbg!(y);
/// Compare only the identifiers of two GenericParams
fn cmp_gp_idents(x: &GenericParam, y: &GenericParam) -> bool {
use GenericParam::*;
Expand Down
7 changes: 4 additions & 3 deletions mockall_derive/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,9 +389,10 @@ fn gen_struct<T>(mock_ident: &syn::Ident,
quote!(<>).to_tokens(&mut macro_g);
}

Expectation::new(&attrs,
&meth_types.expectation_inputs, &merged_g, meth_ident,
Some(&mock_ident), output, &expect_vis).to_tokens(&mut mod_body);
Expectation::new(&attrs, &meth_types.expectation_inputs,
Some(&generics), &meth_types.expectation_generics,
meth_ident, Some(&mock_ident), output,
&expect_vis).to_tokens(&mut mod_body);

if meth_types.is_static {
let name = syn::Ident::new(
Expand Down

0 comments on commit a0188fd

Please sign in to comment.