Skip to content

Commit

Permalink
Fix mocking methods with "super::" in the signature.
Browse files Browse the repository at this point in the history
Fixes #8
  • Loading branch information
asomers committed Aug 1, 2019
1 parent 7c54ed1 commit 697d341
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 11 deletions.
27 changes: 27 additions & 0 deletions mockall/tests/mock_nonpub.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// vim: tw=80
//! methods can use non-public types, as long as the object's visibility is
//! compatible.

use mockall::*;

#[allow(unused)]
mod outer {
struct SuperT();

mod inner {
use super::super::mock;

pub(crate) struct PubCrateT();
struct PrivT();

mock! {
Foo {
fn foo(&self, x: PubCrateT) -> PubCrateT;
fn bar(&self, x: PrivT) -> PrivT;
fn baz(&self, x: super::SuperT) -> super::SuperT;
fn bang(&self, x: crate::outer::SuperT) -> crate::outer::SuperT;
fn bean(&self, x: self::PrivT) -> self::PrivT;
}
}
}
}
38 changes: 28 additions & 10 deletions mockall_derive/src/expectation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,13 @@ pub(crate) fn expectation(attrs: &TokenStream, vis: &Visibility,

let static_bound = Lifetime::new("'static", Span::call_site());

let output = match return_type{
ReturnType::Default => quote!(()),
ReturnType::Type(_, ty) => {
let mut rt2 = return_type.clone();
let output = match rt2 {
ReturnType::Default => Box::new(Type::Tuple(TypeTuple {
paren_token: token::Paren::default(),
elems: Punctuated::new()
})),
ReturnType::Type(_, ref mut ty) => {
let mut rt = ty.clone();
destrify(&mut rt);
if let Some(i) = self_ident {
Expand All @@ -90,18 +94,22 @@ pub(crate) fn expectation(attrs: &TokenStream, vis: &Visibility,
if tr.lifetime.as_ref().map_or(false, |lt| lt.ident == "static")
{
// Just a static expectation
rt
} else {
if !tr.mutability.is_some() {
ref_expectation = true;
} else {
ref_mut_expectation = true;
}
rt = tr.elem.clone();
tr.elem.clone()
}
} else {
rt
}
quote!(#rt)
}
};
let supersuper_output = supersuperfy(&output);
let output = quote!(#output);

let mut egenerics = generics.clone();
let mut macro_g = Punctuated::<Ident, Token![,]>::new();
Expand Down Expand Up @@ -137,6 +145,10 @@ pub(crate) fn expectation(attrs: &TokenStream, vis: &Visibility,

let selfless_args = strip_self(args);
let (argnames, argty) = split_args(args);
let supersuper_argty = Punctuated::<Type, token::Comma>::from_iter(
argty.iter()
.map(|t| supersuperfy(&t))
);
let mut argty_tp = argty.clone();
if !argty_tp.empty_or_trailing() {
// The non-proc macro static_expectation! always uses trailing
Expand All @@ -145,6 +157,10 @@ pub(crate) fn expectation(attrs: &TokenStream, vis: &Visibility,
argty_tp.push_punct(Token![,](Span::call_site()));
}
let (altargnames, altargty) = split_args(altargs);
let supersuper_altargty = Punctuated::<Type, token::Comma>::from_iter(
altargty.iter()
.map(|t| supersuperfy(&t))
);
if ref_expectation {
quote!(
#attrs
Expand Down Expand Up @@ -228,7 +244,9 @@ pub(crate) fn expectation(attrs: &TokenStream, vis: &Visibility,
where #output: Send + Sync
{}

::mockall::generic_expectation_methods!{#vis [#macro_g] [#argty] #output}
::mockall::generic_expectation_methods!{
#vis [#macro_g] [#argty] #supersuper_output
}
impl GenericExpectations {
/// Simulating calling the real method.
#vis fn call #bounded_macro_g
Expand Down Expand Up @@ -503,8 +521,8 @@ pub(crate) fn expectation(attrs: &TokenStream, vis: &Visibility,
#attrs
pub mod #ident {
::mockall::static_expectation!{
#vis [#macro_g] [#argnames] [#argty] [#altargnames]
[#altargty] [#matchexprs] #output
#vis [#macro_g] [#argnames] [#supersuper_argty] [#altargnames]
[#supersuper_altargty] [#matchexprs] #supersuper_output
}

/// Like an [`&Expectation`](struct.Expectation.html) but protected
Expand Down Expand Up @@ -776,8 +794,8 @@ pub(crate) fn expectation(attrs: &TokenStream, vis: &Visibility,
#attrs
pub mod #ident {
::mockall::static_expectation!{
#vis [#macro_g] [#argnames] [#argty] [#altargnames]
[#altargty] [#matchexprs] #output
#vis [#macro_g] [#argnames] [#supersuper_argty] [#altargnames]
[#supersuper_altargty] [#matchexprs] #supersuper_output
}
})
}
Expand Down
81 changes: 80 additions & 1 deletion mockall_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ extern crate proc_macro;
use cfg_if::cfg_if;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{*, punctuated::Punctuated, spanned::Spanned};
use syn::{
*,
punctuated::Pair,
punctuated::Punctuated,
spanned::Spanned
};

mod automock;
mod expectation;
Expand Down Expand Up @@ -186,6 +191,80 @@ fn deselfify(literal_type: &mut Type, actual: &Ident) {
}
}

fn supersuperfy_path(path: &mut Path) {
if let Some(Pair::Punctuated(t, _)) = path.segments.first() {
if t.ident == "super" {
let ps = PathSegment {
ident: Ident::new("super", path.segments.span()),
arguments: PathArguments::None
};
path.segments.insert(0, ps.clone());
path.segments.insert(0, ps);
}
}
}

/// Replace any references to `super::X` in `original` with `super::super::X`.
fn supersuperfy(original: &Type) -> Type {
let mut output = original.clone();
fn recurse(t: &mut Type) {
match t {
Type::Slice(s) => {
supersuperfy(s.elem.as_mut());
},
Type::Array(a) => {
supersuperfy(a.elem.as_mut());
},
Type::Ptr(p) => {
supersuperfy(p.elem.as_mut());
},
Type::Reference(r) => {
supersuperfy(r.elem.as_mut());
},
Type::BareFn(_bfn) => {
unimplemented!()
},
Type::Tuple(tuple) => {
for elem in tuple.elems.iter_mut() {
supersuperfy(elem);
}
}
Type::Path(type_path) => {
if let Some(ref _qself) = type_path.qself {
compile_error(type_path.span(), "QSelf is TODO");
}
supersuperfy_path(&mut type_path.path)
},
Type::Paren(p) => {
supersuperfy(p.elem.as_mut());
},
Type::Group(g) => {
supersuperfy(g.elem.as_mut());
},
Type::Macro(_) | Type::Verbatim(_) => {
compile_error(t.span(),
"mockall_derive does not support this type in this position");
},
Type::ImplTrait(_) => {
panic!("deimplify should've already been run on this output type");
},
Type::TraitObject(tto) => {
for bound in tto.bounds.iter_mut() {
if let TypeParamBound::Trait(tb) = bound {
supersuperfy_path(&mut tb.path)
}
}
},
Type::Infer(_) | Type::Never(_) =>
{
/* Nothing to do */
}
}
}
recurse(&mut output);
output
}

/// Generate a mock identifier from the regular one: eg "Foo" => "MockFoo"
fn gen_mock_ident(ident: &Ident) -> Ident {
Ident::new(&format!("Mock{}", ident), ident.span())
Expand Down

0 comments on commit 697d341

Please sign in to comment.