`concretize` drops lifetimes from already concrete types in methods
I've been trying to work around #217 in a complex codebase and have a simplified reproduction of a related issue. In my case, the trait in question needs a generic type and lifetime (due to async code).
The following simplified example reproduces the issue:
struct Thing<'a>(&'a ());
#[mockall::automock]
trait Something {
/// Some method that needs a generic
// This compiles fine
#[mockall::concretize]
fn generic<R: AsRef<()> + 'static>(&self, r: R);
/// Some method that needs an explicit lifetime
// This compiles fine
fn reference<'a>(&self, thing: &Thing<'a>);
/// Some method that needs an explicit lifetime **and** a generic
// This errors
#[mockall::concretize]
fn generic_and_reference<'a, R: AsRef<()> + 'static>(&self, thing: Thing<'a>, r: R);
}
Compilation of this example errors on the third method with a lifetime error:
error[E0261]: use of undeclared lifetime name `'a`
--> src/lib.rs:14:78
|
3 | #[mockall::automock]
| - - help: consider introducing lifetime `'a` here: `<'a>`
| |
| lifetime `'a` is missing in item created through this procedural macro
...
14 | fn generic_and_reference<'a, R: AsRef<()> + 'static>(&self, thing: Thing<'a>, r: R);
| ^^ undeclared lifetime
error[E0261]: use of undeclared lifetime name `'a`
--> src/lib.rs:14:78
|
3 | #[mockall::automock]
| -
| |
| lifetime `'a` is missing in item created through this procedural macro
| help: consider introducing lifetime `'a` here: `<'a>`
...
14 | fn generic_and_reference<'a, R: AsRef<()> + 'static>(&self, thing: Thing<'a>, r: R);
| ^^ undeclared lifetime
error[E0261]: use of undeclared lifetime name `'a`
--> src/lib.rs:14:78
|
3 | #[mockall::automock]
| - lifetime `'a` is missing in item created through this procedural macro
...
14 | fn generic_and_reference<'a, R: AsRef<()> + 'static>(&self, thing: Thing<'a>, r: R);
| ^^ undeclared lifetime
|
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'a` lifetime
|
3 | for<'a> #[mockall::automock]
| +++++++
help: consider introducing lifetime `'a` here
|
3 | #[mockall::automock]'a,
| +++
help: consider introducing lifetime `'a` here
|
3 | #[mockall::automock]<'a>
| ++++
For more information about this error, try `rustc --explain E0261`.
Looking at the macro expansion, it looks like concretize removed the generic lifetime.
pub mod __mock_MockSomething_Something {
...
pub mod __generic {
...
enum Rfunc {
Default,
Expired,
Mut(Box<dyn FnMut(&(dyn AsRef<()> + 'static)) -> () + Send>),
...
}
...
}
...
pub mod __reference {
...
enum Rfunc {
Default,
Expired,
Mut(Box<dyn for<'a> FnMut(&Thing<'a>) -> () + Send>),
...
}
...
}
...
pub mod __generic_and_reference {
...
enum Rfunc {
Default,
Expired,
Mut(Box<dyn FnMut(Thing<'a>, &(dyn AsRef<()> + 'static)) -> () + Send>),
So the problem is that #[concretize] doesn't work on functions with lifetime parameters. That's unsurprising. There are a billion special cases involving lifetime parameters.
Would you oppose a PR that fixes this particular case? If so, do you have any hints/pointers to share for a first time contributor to mockall (but proficient in Rust)? I'd create a test case based off of mockall/tests/automock_concretize_closures.rs and attempt to make it pass while getting to know the mockall codebase.
#610 should probably be fixed before this one. And I think that the first step towards fixing #610 would be to track the mock struct's Generics separately from the trait's. Then generating the code might be as simple as substituting the mock struct's generics in the appropriate places. To make #[concretize] work, we would have to then do some substitutions in both the trait generics and the struct generics.