Repository: asomers/mockall (Rust)

Issue:
Need to deselfify mock method arguments
Mockall knows that if a mocked function's return type is `Self`, it should replace that with the mock type.  See the various constructor tests in mockall/tests.  But it doesn't currently perform that transformation for argument types, unless they're `self`.  That comes up with methods like `eq`, which compare two arguments of `Self` type.  It should.  For example, neither of the functions here will compile successfully:
```rust
mock! {
    #[derive(Debug)]
    pub Foo {
        fn compare(&self, other: &Foo) -> bool;
    }
    impl PartialEq for Foo {
        fn eq(&self, other: &Self) -> bool;
    }
}
```


Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f62c807..d09f628c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](http://semver.org/).
 
 
+## [ Unreleased ] - ReleaseDate
+
+### Fixed
+
+- Fixed mocking methods that use `Self` in their arguments, but not as the
+  receiver.  For example, `PartialEq::eq` has a signature like
+  `fn eq(&self, other: &Self) -> bool`
+  ([#373](https://github.com/asomers/mockall/pull/373))
+
 ## [ 0.11.0 ] - 2021-12-11
 
 ### Added
diff --git a/mockall_derive/src/lib.rs b/mockall_derive/src/lib.rs
index 3e466c40..34bb5026 100644
--- a/mockall_derive/src/lib.rs
+++ b/mockall_derive/src/lib.rs
@@ -438,6 +438,20 @@ fn deselfify(literal_type: &mut Type, actual: &Ident, generics: &Generics) {
     }
 }
 
+/// Change any `Self` in a method's arguments' types with `actual`.
+/// `generics` is the Generics field of the parent struct.
+fn deselfify_args(
+    args: &mut Punctuated<FnArg, Token![,]>,
+    actual: &Ident,
+    generics: &Generics)
+{
+    for arg in args.iter_mut() {
+        if let FnArg::Typed(pt) = arg {
+            deselfify(&mut *pt.ty, actual, generics)
+        }
+    }
+}
+
 fn find_ident_from_path(path: &Path) -> (Ident, PathArguments) {
     if path.segments.len() != 1 {
         compile_error(path.span(),
diff --git a/mockall_derive/src/mockable_struct.rs b/mockall_derive/src/mockable_struct.rs
index 3940c49d..534fcf59 100644
--- a/mockall_derive/src/mockable_struct.rs
+++ b/mockall_derive/src/mockable_struct.rs
@@ -139,6 +139,7 @@ fn mockable_item_impl(mut impl_: ItemImpl, name: &Ident, generics: &Generics)
 fn mockable_method(meth: &mut ImplItemMethod, name: &Ident, generics: &Generics)
 {
     demutify(&mut meth.sig.inputs);
+    deselfify_args(&mut meth.sig.inputs, name, generics);
     add_lifetime_parameters(&mut meth.sig);
     deimplify(&mut meth.sig.output);
     if let ReturnType::Type(_, ty) = &mut meth.sig.output {
@@ -155,6 +156,7 @@ fn mockable_trait_method(
     generics: &Generics)
 {
     demutify(&mut meth.sig.inputs);
+    deselfify_args(&mut meth.sig.inputs, name, generics);
     add_lifetime_parameters(&mut meth.sig);
     deimplify(&mut meth.sig.output);
     if let ReturnType::Type(_, ty) = &mut meth.sig.output {


Test patch:
diff --git a/mockall/tests/automock_partial_eq.rs b/mockall/tests/automock_partial_eq.rs
new file mode 100644
index 00000000..2618b4e2
--- /dev/null
+++ b/mockall/tests/automock_partial_eq.rs
@@ -0,0 +1,39 @@
+// vim: tw=80
+//! Mockall should deselfify `Self` types, even if they aren't named `self`.
+use mockall::*;
+
+mock! {
+    #[derive(Debug)]
+    pub Foo {
+        fn compare(&self, other: &Self) -> bool;
+    }
+    impl PartialEq for Foo {
+        fn eq(&self, other: &Self) -> bool;
+    }
+}
+
+#[test]
+fn inherent_method() {
+    let mut x = MockFoo::default();
+    let mut y = MockFoo::default();
+    x.expect_compare()
+        .return_const(true);
+    y.expect_compare()
+        .return_const(false);
+
+    assert!(x.compare(&y));
+    assert!(!y.compare(&x));
+}
+
+#[test]
+fn trait_method() {
+    let mut x = MockFoo::default();
+    let mut y = MockFoo::default();
+    x.expect_eq()
+        .return_const(true);
+    y.expect_eq()
+        .return_const(false);
+
+    assert_eq!(x, y);
+    assert!(y != x);
+}