asomers / mockall

A powerful mock object library for Rust
Apache License 2.0
1.5k stars 62 forks source link

use-ing anyhow::Ok interferes with generated code #388

Closed mkosiba closed 2 years ago

mkosiba commented 2 years ago

I'm filing the issue mostly because this results in a somewhat non-obvious error message.

Repro:

use anyhow::{Error, Ok};
use async_trait::async_trait;
use mockall::automock;

#[automock]
#[async_trait]
pub trait Foo {
    async fn bar<'a>(&self) -> Result<(), Error>;
}

Compiler error:

54 | #[automock]
   | ^^^^^^^^^^^
   | |
   | expected `&str`, found struct `anyhow::Error`
   | expected `Result<Result<(), anyhow::Error>, &'static str>` because of return type
   |
   = note: expected enum `Result<_, &'static str>`
              found enum `Result<_, anyhow::Error>`
   = note: this error originates in the attribute macro `automock` (in Nightly builds, run with -Z macro-backtrace for more info)

Changing the first line to use anyhow::Error; causes the issue to go away.

It seems like some of the generated code (like fn call_mut(&mut self) -> std::result::Result<Result<(), Error>, &'static str>) explicitly references std::result::Result while other bits (like returning Ok(__mockall_f())) rely on nobody being silly enough to import their own Ok. Hopefully fixing this is as simple as having the generated code return a fully-qualified std::result::Result::Ok instead of just Ok.

asomers commented 2 years ago

That's a very good guess. BTW, you can see the generated code by setting MOCKALL_DEBUG=1 when you build. Would you be able to check it?

mkosiba commented 2 years ago

Sure! Relevant bit of generated code:

        {
            Default, Expired,
            Mut(Box < dyn FnMut() -> Result < (), Error > + Send >),
            MutSt(:: mockall :: Fragile < Box < dyn FnMut() -> Result < (),
            Error > >>),
            Once(Box < dyn FnOnce() -> Result < (), Error > + Send >),
            OnceSt(:: mockall :: Fragile < Box < dyn FnOnce() -> Result < (),
            Error > >>), _Phantom(Box < dyn Fn() + Send >)
        } impl Rfunc
        {
            fn call_mut(& mut self,) -> std :: result :: Result < Result < (),
            Error >, & 'static str >
            {
                match self
                {
                    Rfunc :: Default =>
                    {
                        use :: mockall :: ReturnDefault ; :: mockall ::
                        DefaultReturner :: < Result < (), Error > > ::
                        return_default()
                    }, Rfunc :: Expired =>
                    { Err("called twice, but it returns by move") }, Rfunc ::
                    Mut(__mockall_f) => { Ok(__mockall_f()) }, Rfunc ::
                    MutSt(__mockall_f) => { Ok((__mockall_f.get_mut()) ()) },
                    Rfunc :: Once(_) =>
                    {
                        if let Rfunc :: Once(mut __mockall_f) = mem ::
                        replace(self, Rfunc :: Expired) { Ok(__mockall_f()) } else
                        { unreachable! () }
                    }, Rfunc :: OnceSt(_) =>
                    {
                        if let Rfunc :: OnceSt(mut __mockall_f) = mem ::
                        replace(self, Rfunc :: Expired)
                        { Ok((__mockall_f.into_inner()) ()) } else
                        { unreachable! () }
                    }, Rfunc :: _Phantom(_) => unreachable! ()
                }
            }
        }
mkosiba commented 2 years ago

I can also confirm that if I paste the generated code into my IDE I get the error and if I change Ok to std::result::Result::Ok the errors go away.