Skip to content

Commit

Permalink
Merge pull request #208 from asomers/where_clause_with_qself2
Browse files Browse the repository at this point in the history
Fix using QSelf in a where clause or a return type.
  • Loading branch information
asomers authored Sep 7, 2020
2 parents 4fef963 + a3a99a4 commit af51db5
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 48 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [0.8.1] - ReleaseDate
### Added
### Changed
### Fixed

- Fixed using `<X as Y>::Z` syntax in a where clause or a return type.
([#207](https://github.com/asomers/mockall/pull/207))

### Removed

## [0.8.0] - 29 August 2020
### Added
- Added support for mocking structs and traits with associated constants.
Expand Down
17 changes: 1 addition & 16 deletions mockall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,29 +1267,14 @@ pub use mockall_derive::automock;
/// # fn main() {}
/// ```
/// Associated types can easily be mocked by specifying a concrete type in the
/// `mock!{}` invocation. But be careful not to reference the associated type
/// in the signatures of any of the trait's methods; repeat the concrete type
/// instead. For example, do:
/// `mock!{}` invocation.
/// ```
/// # use mockall_derive::mock;
/// mock!{
/// MyIter {}
/// trait Iterator {
/// type Item=u32;
///
/// fn next(&mut self) -> Option<u32>;
/// }
/// }
/// # fn main() {}
/// ```
/// *not*
/// ```compile_fail
/// # use mockall_derive::mock;
/// mock!{
/// MyIter {}
/// trait Iterator {
/// type Item=u32;
///
/// fn next(&mut self) -> Option<<Self as Iterator>::Item>;
/// }
/// }
Expand Down
54 changes: 54 additions & 0 deletions mockall/tests/automock_qself.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// vim: tw=80
//! Using QSelf in an argument, a where clause, or a return type
#![deny(warnings)]

use mockall::*;

pub trait Foo {
type Output;
}

pub struct SendFoo {}
impl Foo for SendFoo {
type Output = u32;
}

pub struct A{}
#[automock]
impl A {
pub fn foo<T: Foo + 'static>(&self, _q: <T as Foo>::Output) {
}
pub fn bar<T: Foo + 'static>(&self, _t: T) -> <T as Foo>::Output {
unimplemented!()
}
pub fn bean<T>(&self, _t: T)
where T: Foo + 'static,
<T as Foo>::Output: Send
{
}
}

#[test]
fn arguments() {
let mut mock = MockA::new();
mock.expect_foo::<SendFoo>()
.with(predicate::eq(42u32))
.return_const(());
mock.foo::<SendFoo>(42u32);
}

#[test]
fn where_clause() {
let mut mock = MockA::new();
mock.expect_bean::<SendFoo>()
.return_const(());
mock.bean(SendFoo{});
}

#[test]
fn return_value() {
let mut mock = MockA::new();
mock.expect_bar::<SendFoo>()
.return_const(42u32);
assert_eq!(42, mock.bar(SendFoo{}));
}
99 changes: 71 additions & 28 deletions mockall_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ fn deanonymize(literal_type: &mut Type) {
Type::Never(_) => (),
Type::Paren(tp) => deanonymize(tp.elem.as_mut()),
Type::Path(tp) => {
if let Some(ref _qself) = tp.qself {
compile_error(tp.span(), "QSelf is TODO");
if let Some(ref mut qself) = tp.qself {
deanonymize(qself.ty.as_mut());
}
deanonymize_path(&mut tp.path);
},
Expand Down Expand Up @@ -316,8 +316,8 @@ fn deselfify(literal_type: &mut Type, actual: &Ident, generics: &Generics) {
}
}
Type::Path(type_path) => {
if let Some(ref _qself) = type_path.qself {
compile_error(type_path.span(), "QSelf is TODO");
if let Some(ref mut qself) = type_path.qself {
deselfify(qself.ty.as_mut(), actual, generics);
}
let p = &mut type_path.path;
for seg in p.segments.iter_mut() {
Expand Down Expand Up @@ -567,20 +567,8 @@ fn pat_is_self(pat: &Pat) -> bool {
}
}

fn supersuperfy_path(path: &mut Path, levels: i32) {
if let Some(t) = path.segments.first() {
if t.ident == "super" {
let mut ident = format_ident!("super");
ident.set_span(path.segments.span());
let ps = PathSegment {
ident,
arguments: PathArguments::None
};
for _ in 0..levels {
path.segments.insert(0, ps.clone());
}
}
}
/// Add `levels` `super::` to the path. Return the number of levels added.
fn supersuperfy_path(path: &mut Path, levels: usize) -> usize {
if let Some(t) = path.segments.last_mut() {
match &mut t.arguments {
PathArguments::None => (),
Expand Down Expand Up @@ -610,12 +598,30 @@ fn supersuperfy_path(path: &mut Path, levels: i32) {
},
}
}
if let Some(t) = path.segments.first() {
if t.ident == "super" {
let mut ident = format_ident!("super");
ident.set_span(path.segments.span());
let ps = PathSegment {
ident,
arguments: PathArguments::None
};
for _ in 0..levels {
path.segments.insert(0, ps.clone());
}
levels
} else {
0
}
} else {
0
}
}

/// Replace any references to `super::X` in `original` with `super::super::X`.
fn supersuperfy(original: &Type, levels: i32) -> Type {
fn supersuperfy(original: &Type, levels: usize) -> Type {
let mut output = original.clone();
fn recurse(t: &mut Type, levels: i32) {
fn recurse(t: &mut Type, levels: usize) {
match t {
Type::Slice(s) => {
recurse(s.elem.as_mut(), levels);
Expand Down Expand Up @@ -643,10 +649,11 @@ fn supersuperfy(original: &Type, levels: i32) -> Type {
}
}
Type::Path(type_path) => {
if let Some(ref _qself) = type_path.qself {
compile_error(type_path.span(), "QSelf is TODO");
let added = supersuperfy_path(&mut type_path.path, levels);
if let Some(ref mut qself) = type_path.qself {
recurse(qself.ty.as_mut(), levels);
qself.position += added;
}
supersuperfy_path(&mut type_path.path, levels);
},
Type::Paren(p) => {
recurse(p.elem.as_mut(), levels);
Expand All @@ -664,7 +671,7 @@ fn supersuperfy(original: &Type, levels: i32) -> Type {
Type::TraitObject(tto) => {
for bound in tto.bounds.iter_mut() {
if let TypeParamBound::Trait(tb) = bound {
supersuperfy_path(&mut tb.path, levels)
supersuperfy_path(&mut tb.path, levels);
}
}
},
Expand All @@ -679,7 +686,7 @@ fn supersuperfy(original: &Type, levels: i32) -> Type {
output
}

fn supersuperfy_generics(generics: &mut Generics, levels: i32) {
fn supersuperfy_generics(generics: &mut Generics, levels: usize) {
for param in generics.params.iter_mut() {
if let GenericParam::Type(tp) = param {
supersuperfy_bounds(&mut tp.bounds, levels);
Expand All @@ -700,7 +707,7 @@ fn supersuperfy_generics(generics: &mut Generics, levels: i32) {

fn supersuperfy_bounds(
bounds: &mut Punctuated<TypeParamBound, Token![+]>,
levels: i32)
levels: usize)
{
for bound in bounds.iter_mut() {
if let TypeParamBound::Trait(tb) = bound {
Expand Down Expand Up @@ -913,10 +920,9 @@ fn split_lifetimes(
/// # Arguments
/// - `vis`: Original visibility of the item
/// - `levels`: How many modules will the mock item be nested in?
fn expectation_visibility(vis: &Visibility, levels: i32)
fn expectation_visibility(vis: &Visibility, levels: usize)
-> Visibility
{
debug_assert!(levels >= 0);
if levels == 0 {
return vis.clone();
}
Expand Down Expand Up @@ -1096,6 +1102,35 @@ mod automock {
}
}

mod deselfify {
use super::*;

fn check_deselfify(
orig_ts: TokenStream,
actual_ts: TokenStream,
generics_ts: TokenStream,
expected_ts: TokenStream)
{
let mut ty: Type = parse2(orig_ts).unwrap();
let actual: Ident = parse2(actual_ts).unwrap();
let generics: Generics = parse2(generics_ts).unwrap();
let expected: Type = parse2(expected_ts).unwrap();
deselfify(&mut ty, &actual, &generics);
assert_eq!(quote!(#ty).to_string(),
quote!(#expected).to_string());
}

#[test]
fn qself() {
check_deselfify(
quote!(<Self as Self>::Self),
quote!(Foo),
quote!(),
quote!(<Foo as Foo>::Foo)
);
}
}

mod merge_generics {
use super::*;

Expand Down Expand Up @@ -1253,6 +1288,14 @@ mod supersuperfy {
);
}

#[test]
fn path_with_qself() {
check_supersuperfy(
quote!(<super::X as super::Y>::Foo<u32>),
quote!(<super::super::X as super::super::Y>::Foo<u32>),
);
}

#[test]
fn angle_bracketed_generic_arguments() {
check_supersuperfy(
Expand Down
8 changes: 4 additions & 4 deletions mockall_derive/src/mock_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ fn type_generics(generics: &Generics) -> Generics {
#[derive(Clone, Copy, Debug)]
pub(crate) struct Builder<'a> {
attrs: &'a [Attribute],
call_levels: Option<i32>,
levels: i32,
call_levels: Option<usize>,
levels: usize,
parent: Option<&'a Ident>,
sig: &'a Signature,
struct_: Option<&'a Ident>,
Expand Down Expand Up @@ -285,14 +285,14 @@ impl<'a> Builder<'a> {

/// How many levels of modules beneath the original function this one is
/// nested.
pub fn call_levels(&mut self, levels: i32) -> &mut Self {
pub fn call_levels(&mut self, levels: usize) -> &mut Self {
self.call_levels = Some(levels);
self
}

/// How many levels of modules beneath the original function this one's
/// private module is nested.
pub fn levels(&mut self, levels: i32) -> &mut Self {
pub fn levels(&mut self, levels: usize) -> &mut Self {
self.levels = levels;
self
}
Expand Down

0 comments on commit af51db5

Please sign in to comment.