diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a5340ac..a177946c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,14 +6,21 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [ Unreleased ] - ReleaseDate -## Changed +### Changed - Raised MSRV to 1.45.0 because futures-task did. ([#407](https://github.com/asomers/mockall/pull/407)) +### Fixed + +- Methods with a `where Self: ...` clause will now be mocked like concrete + methods, not generic ones. Among other effects, this prevents "unused method + expect" warnings from the latest nightly compiler. + ([#415](https://github.com/asomers/mockall/pull/415)) + ## [ 0.11.2 ] - 2022-07-24 -## Fixed +### Fixed - Suppress "dead code" warnings when automocking a struct's private method. It might be used only by other public methods in the same struct. diff --git a/mockall/tests/automock_where_self.rs b/mockall/tests/automock_where_self.rs new file mode 100644 index 00000000..5eca1c3a --- /dev/null +++ b/mockall/tests/automock_where_self.rs @@ -0,0 +1,28 @@ +// vim: ts=80 +//! Methods with a "where Self: ..." where clause should be mocked as concrete, +//! not generic. +#![deny(warnings)] + +// Enclose the mocked trait within a non-public module. With some versions of +// rustc, that causes "unused method" errors for the generic code, but not the +// concrete code. +// rustc 1.66.0-nightly (e7119a030 2022-09-22) +mod mymod { + + #[mockall::automock] + pub trait Server { + fn version<'a>(&'a self) -> Option<&'static str> where Self: Sized; + } + +} + +use mymod::{MockServer, Server}; + +#[test] +fn return_const() { + let mut mock = MockServer::new(); + mock.expect_version() + .return_const(None); + + mock.version(); +} diff --git a/mockall_derive/src/lib.rs b/mockall_derive/src/lib.rs index 887a7828..57319bff 100644 --- a/mockall_derive/src/lib.rs +++ b/mockall_derive/src/lib.rs @@ -285,6 +285,27 @@ fn deimplify(rt: &mut ReturnType) { } } +/// Remove any generics that place constraints on Self. +fn dewhereselfify(generics: &mut Generics) { + if let Some(ref mut wc) = &mut generics.where_clause { + let new_predicates = wc.predicates.iter() + .filter(|wp| match wp { + WherePredicate::Type(pt) => { + pt.bounded_ty != parse2(quote!(Self)).unwrap() + }, + _ => true + }).cloned() + .collect::>(); + wc.predicates = new_predicates; + } + if generics.where_clause.as_ref() + .map(|wc| wc.predicates.is_empty()) + .unwrap_or(false) + { + generics.where_clause = None; + } +} + /// Remove any mutability qualifiers from a method's argument list fn demutify(inputs: &mut Punctuated) { for arg in inputs.iter_mut() { @@ -1381,6 +1402,46 @@ mod deselfify { } } +mod dewhereselfify { + use super::*; + + #[test] + fn lifetime() { + let mut meth: ImplItemMethod = parse2(quote!( + fn foo<'a>(&self) where 'a: 'static, Self: Sized; + )).unwrap(); + let expected: ImplItemMethod = parse2(quote!( + fn foo<'a>(&self) where 'a: 'static; + )).unwrap(); + dewhereselfify(&mut meth.sig.generics); + assert_eq!(meth, expected); + } + + #[test] + fn normal_method() { + let mut meth: ImplItemMethod = parse2(quote!( + fn foo(&self) where Self: Sized; + )).unwrap(); + let expected: ImplItemMethod = parse2(quote!( + fn foo(&self); + )).unwrap(); + dewhereselfify(&mut meth.sig.generics); + assert_eq!(meth, expected); + } + + #[test] + fn with_real_generics() { + let mut meth: ImplItemMethod = parse2(quote!( + fn foo(&self, t: T) where Self: Sized, T: Copy; + )).unwrap(); + let expected: ImplItemMethod = parse2(quote!( + fn foo(&self, t: T) where T: Copy; + )).unwrap(); + dewhereselfify(&mut meth.sig.generics); + assert_eq!(meth, expected); + } +} + mod gen_keyid { use super::*; diff --git a/mockall_derive/src/mockable_struct.rs b/mockall_derive/src/mockable_struct.rs index 534fcf59..33a6f03d 100644 --- a/mockall_derive/src/mockable_struct.rs +++ b/mockall_derive/src/mockable_struct.rs @@ -142,6 +142,7 @@ fn mockable_method(meth: &mut ImplItemMethod, name: &Ident, generics: &Generics) deselfify_args(&mut meth.sig.inputs, name, generics); add_lifetime_parameters(&mut meth.sig); deimplify(&mut meth.sig.output); + dewhereselfify(&mut meth.sig.generics); if let ReturnType::Type(_, ty) = &mut meth.sig.output { deselfify(ty, name, generics); deanonymize(ty); @@ -159,6 +160,7 @@ fn mockable_trait_method( deselfify_args(&mut meth.sig.inputs, name, generics); add_lifetime_parameters(&mut meth.sig); deimplify(&mut meth.sig.output); + dewhereselfify(&mut meth.sig.generics); if let ReturnType::Type(_, ty) = &mut meth.sig.output { deselfify(ty, name, generics); deanonymize(ty);