Skip to content

Commit

Permalink
mock! now requires visibility specifiers for inherent methods.
Browse files Browse the repository at this point in the history
Previously, it forbade them, and treated the inherent method as having
the same visibility as the struct itself.  That was occasionally
problematic, for example when the mock struct was pub but one of its
methods' arguments was not.

Relates to issue #143
  • Loading branch information
asomers committed Sep 7, 2020
1 parent 16b4524 commit b6e8d73
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Changed

- `mock!` now requires visibility specifiers for inherent methods.
([#207](https://github.com/asomers/mockall/pull/207))

- Changed the syntax for mocking foreign functions. Instead of using
`#[automock]` directly on the `extern` block, you must wrap the `extern`
block in a module, and `#[automock]` that module. The old method is
Expand Down
70 changes: 57 additions & 13 deletions mockall_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,15 +976,20 @@ fn mock_it<M: Into<MockableItem>>(inputs: M) -> TokenStream
ts
}

#[proc_macro]
pub fn mock(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let item: MockableStruct = match syn::parse2(input.into()) {
fn do_mock(input: TokenStream) -> TokenStream
{
let item: MockableStruct = match syn::parse2(input) {
Ok(mock) => mock,
Err(err) => {
return err.to_compile_error().into();
}
};
mock_it(item).into()
mock_it(item)
}

#[proc_macro]
pub fn mock(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
do_mock(input.into()).into()
}

#[proc_macro_attribute]
Expand Down Expand Up @@ -1018,21 +1023,59 @@ fn do_automock(attrs: TokenStream, input: TokenStream) -> TokenStream {
mod t {
use super::*;

fn assert_contains(output: &str, tokens: TokenStream) {
let s = tokens.to_string();
assert!(output.contains(&s), "output does not contain {:?}", &s);
}

fn assert_not_contains(output: &str, tokens: TokenStream) {
let s = tokens.to_string();
assert!(!output.contains(&s), "output does not contain {:?}", &s);
}

/// Various tests for overall code generation that are hard or impossible to
/// write as integration tests
mod automock {
mod mock {
use std::str::FromStr;
use super::super::*;
use super::*;

fn assert_contains(output: &str, tokens: TokenStream) {
let s = tokens.to_string();
assert!(output.contains(&s), "output does not contain {:?}", &s);
}
#[test]
fn inherent_method_visibility() {
let code = r#"
Foo {
fn foo(&self);
pub fn bar(&self);
pub(crate) fn baz(&self);
pub(super) fn bean(&self);
pub(in crate::outer) fn boom(&self);
}
"#;
let ts = proc_macro2::TokenStream::from_str(code).unwrap();
let output = do_mock(ts).to_string();
assert_not_contains(&output, quote!(pub fn foo));
assert!(!output.contains(") fn foo"));
assert_contains(&output, quote!(pub fn bar));
assert_contains(&output, quote!(pub(crate) fn baz));
assert_contains(&output, quote!(pub(super) fn bean));
assert_contains(&output, quote!(pub(in crate::outer) fn boom));

fn assert_not_contains(output: &str, tokens: TokenStream) {
let s = tokens.to_string();
assert!(!output.contains(&s), "output does not contain {:?}", &s);
assert_not_contains(&output, quote!(pub fn expect_foo));
assert!(!output.contains("pub fn expect_foo"));
assert!(!output.contains(") fn expect_foo"));
assert_contains(&output, quote!(pub fn expect_bar));
assert_contains(&output, quote!(pub(crate) fn expect_baz));
assert_contains(&output, quote!(pub(super) fn expect_bean));
assert_contains(&output, quote!(pub(in crate::outer) fn expect_boom));
}
}

/// Various tests for overall code generation that are hard or impossible to
/// write as integration tests
mod automock {
use std::str::FromStr;
use super::super::*;
use super::*;

#[test]
fn doc_comments() {
Expand Down Expand Up @@ -1062,8 +1105,9 @@ mod automock {
let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap();
let output = do_automock(attrs_ts, ts).to_string();
assert_not_contains(&output, quote!(pub fn foo));
assert!(!output.contains(") fn foo"));
assert_not_contains(&output, quote!(pub fn expect_foo));
assert_not_contains(&output, quote!(pub fn expect_foo));
assert!(!output.contains(") fn expect_foo"));
assert_contains(&output, quote!(pub fn bar));
assert_contains(&output, quote!(pub fn expect_bar));
assert_contains(&output, quote!(pub(super) fn baz));
Expand Down
12 changes: 6 additions & 6 deletions mockall_derive/src/mockable_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,13 +467,13 @@ impl Parse for MockableStruct {
let mut consts = Vec::new();
let mut methods = Vec::new();
while !impl_content.is_empty() {
let method: syn::TraitItem = impl_content.parse()?;
match method {
syn::TraitItem::Method(mut meth) => {
mockable_trait_method(&mut meth, &name, &generics);
methods.push(tim2iim(meth, &vis))
let item: ImplItem = impl_content.parse()?;
match item {
ImplItem::Method(mut iim) => {
mockable_method(&mut iim, &name, &generics);
methods.push(iim);
},
TraitItem::Const(iic) => consts.push(tic2iic(iic, &vis)),
ImplItem::Const(iic) => consts.push(iic),
_ => {
return Err(input.error("Unsupported in this context"));
}
Expand Down

0 comments on commit b6e8d73

Please sign in to comment.