Skip to content

Commit

Permalink
Mock methods with where Self: ... clauses as concrete.
Browse files Browse the repository at this point in the history
Previously they were treated as generic.  Among other effects, this
prevents "unused method expect" warnings from the latest nightly
compiler.

Fixes #414
  • Loading branch information
asomers committed Oct 18, 2022
1 parent 6ec6c40 commit e8ab4b9
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 2 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
28 changes: 28 additions & 0 deletions mockall/tests/automock_where_self.rs
Original file line number Diff line number Diff line change
@@ -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();
}
61 changes: 61 additions & 0 deletions mockall_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Punctuated<WherePredicate, Token![,]>>();
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<FnArg, token::Comma>) {
for arg in inputs.iter_mut() {
Expand Down Expand Up @@ -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<T>(&self, t: T) where Self: Sized, T: Copy;
)).unwrap();
let expected: ImplItemMethod = parse2(quote!(
fn foo<T>(&self, t: T) where T: Copy;
)).unwrap();
dewhereselfify(&mut meth.sig.generics);
assert_eq!(meth, expected);
}
}

mod gen_keyid {
use super::*;

Expand Down
2 changes: 2 additions & 0 deletions mockall_derive/src/mockable_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit e8ab4b9

Please sign in to comment.