Repository: asomers/mockall (Rust)

Issue:
trait `A` is not implemented for `Box<dyn A>`
<!--  ❤️  -->
Hi folks, I met a compile error when doing this:
```rust
trait A { }

trait B: A {
    fn as_a(&self) -> &dyn A;
}


mockall::mock! {
    Foo {}
    impl A for Foo {}
    impl B for Foo {
        fn as_a(&self) -> &dyn A;
    }
}
```
The compile error is:
```
error[E0277]: the trait bound `Box<dyn A>: A` is not satisfied
  --> src/lib.rs:11:1
   |
11 | / mockall::mock! {
12 | |     Foo {}
13 | |     impl A for Foo {}
14 | |     impl B for Foo {
15 | |         fn as_a(&self) -> &dyn A;
16 | |     }
17 | | }
   | |_^ the trait `A` is not implemented for `Box<dyn A>`
   |
   = note: required for the cast to the object type `dyn A`
   = note: this error originates in the macro `mockall::mock` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
error: could not compile `mocktest` due to previous error
```
I ran `cargo expand` to see what was happening, and find that:
```rust
impl __mock_MockFoo_B::__as_a::Expectation {
    /// Call this [`Expectation`] as if it were the real method.
    pub(in super::super) fn call(&self) -> &Box<dyn A> {
         /* ... */
    }
}
// ...
impl B for MockFoo {
    fn as_a(&self) -> &dyn A {
        let no_match_msg = /* something irrelavent ... */;
        self.B_expectations.as_a.call().expect(&no_match_msg)
    }
}
```
Here `Expectation::call` returns `&Box<dyn A>` but `B::as_a` requires `&dyn A`, and `Box<dyn A>: A` is not satisfied.

The solution is simple. I just add something like this:
```rust
impl<T: A + ?Sized> A for Box<T> {}
```
Then it compiles.

---
But is it possible to make this change in the generated code in `MockFoo::as_a`?
```diff
-        self.B_expectations.as_a.call().expect(&no_match_msg)
+        &*self.B_expectations.as_a.call().expect(&no_match_msg)
```
After that, there is no extra `impl<T: A + ?Sized> A for Box<T> {}` needed.


Discussion:
Yeah, I think that will work.  Since you've already delved in so far, would you like to submit a PR?  You can extend the test case that's already in mockall/tests/mock_return_dyn_trait.rs .
No problem. I'll try opening a PR later.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d09f628c..5ad28726 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
   receiver.  For example, `PartialEq::eq` has a signature like
   `fn eq(&self, other: &Self) -> bool`
   ([#373](https://github.com/asomers/mockall/pull/373))
+- Fixed mocking methods that return a reference to a `dyn T` trait object,
+  when that trait is not already implemented for `Box<dyn T>`.
+  ([#380](https://github.com/asomers/mockall/pull/380))
 
 ## [ 0.11.0 ] - 2021-12-11
 
diff --git a/mockall_derive/src/mock_function.rs b/mockall_derive/src/mock_function.rs
index b560c60a..78df187c 100644
--- a/mockall_derive/src/mock_function.rs
+++ b/mockall_derive/src/mock_function.rs
@@ -4,7 +4,11 @@ use super::*;
 use quote::ToTokens;
 
 /// Convert a trait object reference into a reference to a Boxed trait
-fn dedynify(ty: &mut Type) {
+///
+/// # Returns
+///
+/// Returns `true` if it was necessary to box the type.
+fn dedynify(ty: &mut Type) -> bool {
     if let Type::Reference(ref mut tr) = ty {
         if let Type::TraitObject(ref tto) = tr.elem.as_ref() {
             if let Some(lt) = &tr.lifetime {
@@ -17,13 +21,15 @@ fn dedynify(ty: &mut Type) {
                     // when methods like returning add a `+ Send` to the output
                     // type.
                     *tr.elem = parse2(quote!((#tto))).unwrap();
-                    return;
+                    return false;
                 }
             }
 
             *tr.elem = parse2(quote!(Box<#tto>)).unwrap();
+            return true;
         }
     }
+    false
 }
 
 /// Convert a special reference type like "&str" into a reference to its owned
@@ -207,16 +213,19 @@ impl<'a> Builder<'a> {
                 is_static = false;
             }
         }
-        let output = match self.sig.output {
-            ReturnType::Default => Type::Tuple(TypeTuple {
+        let (output, boxed) = match self.sig.output {
+            ReturnType::Default => (
+                Type::Tuple(TypeTuple {
                     paren_token: token::Paren::default(),
-                    elems: Punctuated::new()
+                    elems: Punctuated::new(),
                 }),
+                false,
+            ),
             ReturnType::Type(_, ref ty) => {
                 let mut output_ty = supersuperfy(&**ty, self.levels);
                 destrify(&mut output_ty);
-                dedynify(&mut output_ty);
-                output_ty
+                let boxed = dedynify(&mut output_ty);
+                (output_ty, boxed)
             }
         };
         supersuperfy_generics(&mut declosured_generics, self.levels);
@@ -284,6 +293,7 @@ impl<'a> Builder<'a> {
             mod_ident: self.parent.unwrap_or(&Ident::new("FIXME", Span::call_site())).clone(),
             output,
             owned_output,
+            boxed,
             predexprs,
             predty,
             refpredty,
@@ -389,6 +399,8 @@ pub(crate) struct MockFunction {
     /// If the real output type is a non-'static reference, then it will differ
     /// from this field.
     owned_output: Type,
+    /// True if the `owned_type` is boxed by `Box<>`.
+    boxed: bool,
     /// Expressions that create the predicate arguments from the call arguments
     predexprs: Vec<TokenStream>,
     /// Types used for Predicates.  Will be almost the same as args, but every
@@ -452,6 +464,14 @@ impl MockFunction {
         } else {
             Ident::new("call", Span::call_site())
         };
+        let mut deref = quote!();
+        if self.boxed {
+            if self.return_ref {
+                deref = quote!(&**);
+            } else if self.return_refmut {
+                deref = quote!(&mut **);
+            }
+        }
         if self.is_static {
             let outer_mod_path = self.outer_mod_path(modname);
             quote!(
@@ -459,7 +479,7 @@ impl MockFunction {
                 #(#attrs)*
                 #vis #sig {
                     let no_match_msg = #no_match_msg;
-                    {
+                    #deref {
                         let __mockall_guard = #outer_mod_path::EXPECTATIONS
                             .lock().unwrap();
                         /*
@@ -479,7 +499,7 @@ impl MockFunction {
                 #(#attrs)*
                 #vis #sig {
                     let no_match_msg = #no_match_msg;
-                    self.#substruct_obj #name.#call#tbf(#(#call_exprs,)*)
+                    #deref self.#substruct_obj #name.#call#tbf(#(#call_exprs,)*)
                     .expect(&no_match_msg)
                 }
 


Test patch:
diff --git a/mockall/tests/mock_return_dyn_trait.rs b/mockall/tests/mock_return_dyn_trait.rs
index 6011791c..a22ea60f 100644
--- a/mockall/tests/mock_return_dyn_trait.rs
+++ b/mockall/tests/mock_return_dyn_trait.rs
@@ -3,47 +3,55 @@
 #![deny(warnings)]
 
 use mockall::*;
-use std::fmt::Debug;
 
-trait T: Debug + Sync {
-    fn mutate(&mut self) {}
+trait Test: Sync {
+    fn value(&self) -> i32;
+    fn mutate(&mut self);
 }
 
-impl T for u32 {}
+impl Test for i32 {
+    fn value(&self) -> i32 {
+        *self
+    }
 
-impl<Q> T for Q where Q: Debug + Sync + AsMut<dyn T> {}
+    fn mutate(&mut self) {
+        *self = 0;
+    }
+}
 
-mock!{
+mock! {
     Foo {
-        fn foo(&self) -> &dyn Debug;
-        fn bar(&self) -> &'static dyn T;
-        fn baz(&mut self) -> &mut dyn T;
+        fn ref_dyn(&self) -> &dyn Test;
+        fn static_dyn(&self) -> &'static dyn Test;
+        fn mut_dyn(&mut self) -> &mut dyn Test;
     }
 }
 
 #[test]
-fn return_const() {
+fn ref_dyn() {
     let mut mock = MockFoo::new();
-    mock.expect_foo()
-        .return_const(Box::new(42u32) as Box<dyn Debug>);
+    mock.expect_ref_dyn()
+        .return_const(Box::new(42) as Box<dyn Test>);
 
-    assert_eq!("42", format!("{:?}", mock.foo()));
+    assert_eq!(42, mock.ref_dyn().value());
 }
 
 #[test]
-fn static_ref() {
+fn static_dyn() {
     let mut mock = MockFoo::new();
-    mock.expect_bar()
-        .return_const(&42u32 as &dyn T);
+    mock.expect_static_dyn()
+        .return_const(&42 as &'static dyn Test);
 
-    assert_eq!("42", format!("{:?}", mock.bar()));
+    assert_eq!(42, mock.static_dyn().value());
 }
 
 #[test]
-fn return_var() {
+fn mut_dyn() {
     let mut mock = MockFoo::new();
-    mock.expect_baz()
-        .return_var(Box::new(42u32) as Box<dyn T>);
+    mock.expect_mut_dyn()
+        .return_var(Box::new(42) as Box<dyn Test>);
 
-    mock.baz().mutate();
+    assert_eq!(42, mock.mut_dyn().value());
+    mock.mut_dyn().mutate();
+    assert_eq!(0, mock.mut_dyn().value());
 }