From 1db92b6f847efa2835482fbc3f420aa1e2b9c4d6 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Thu, 24 Mar 2022 21:12:28 -0600 Subject: [PATCH] Fix mocking methods that use `Self` in their arguments. Fixes #355 --- CHANGELOG.md | 9 +++++++ mockall/tests/automock_partial_eq.rs | 39 +++++++++++++++++++++++++++ mockall_derive/src/lib.rs | 14 ++++++++++ mockall_derive/src/mockable_struct.rs | 2 ++ 4 files changed, 64 insertions(+) create mode 100644 mockall/tests/automock_partial_eq.rs 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/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); +} 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, + 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 {