// vim: tw=80 usesuper::*; use syn::parse::{Parse, ParseStream};
/// Make any implicit lifetime parameters explicit fn add_lifetime_parameters(sig: &mut Signature) { fn add_to_trait_object(generics: &mut Generics, var: &Pat, to: &an style='color:red'>mut TypeTraitObject) { letmut has_lifetime = false; for bound in to.bounds.iter() { iflet TypeParamBound::Lifetime(_) = bound {
has_lifetime = true;
}
} if ! has_lifetime { let arg_ident = match *var {
Pat::Wild(_) => {
compile_error(var.span(), "Mocked methods must have named arguments");
format_ident!("dont_care")
},
Pat::Ident(ref pat_ident) => { iflet Some(r) = &pat_ident.by_ref {
compile_error(r.span(), "Mockall does not support by-reference argument bindings");
} iflet Some((_at, subpat)) = &pat_ident.subpat {
compile_error(subpat.span(), "Mockall does not support subpattern bindings");
}
pat_ident.ident.clone()
},
_ => {
compile_error(var.span(), "Unsupported argument type");
format_ident!("dont_care")
}
}; let s = format!("'__mockall_{arg_ident}"); let span = Span::call_site(); let lt = Lifetime::new(&s, span);
to.bounds.push(TypeParamBound::Lifetime(lt.clone()));
generics.lt_token.get_or_insert(Token);
generics.gt_token.get_or_insert(Token); let gpl = GenericParam::Lifetime(LifetimeParam::new(lt));
generics.params.push(gpl);
}
}
fn add_to_type(generics: &mut Generics, var: &Pat, ty: &='color:red'>mutType) { match ty { Type::Array(ta) => add_to_type(generics, var, ta.elem.as_mut()), Type::BareFn(_) => (), Type::ImplTrait(_) => (), Type::Path(_) => (), Type::Ptr(_) => (), Type::Reference(tr) => { match tr.elem.as_mut() { Type::Paren(tp) => { ifletType::TraitObject(to) = tp.elem.as_mut() {
add_to_trait_object(generics, var, to);
} else {
add_to_type(generics, var, tr.elem.as_mut());
}
}, Type::TraitObject(to) => {
add_to_trait_object(generics, var, to); // We need to wrap it in a Paren. Otherwise it won't be // syntactically valid after we add a lifetime bound, // due to a "ambiguous `+` in a type" error
*tr.elem = Type::Paren(TypeParen {
paren_token: token::Paren::default(),
elem: Box::new(Type::TraitObject(to.clone()))
});
},
_ => add_to_type(generics, var, tr.elem.as_mut()),
}
}, Type::Slice(ts) => add_to_type(generics, var, ts.elem.as_mut()), Type::Tuple(tt) => { for ty in tt.elems.iter_mut() {
add_to_type(generics, var, ty)
}
},
_ => compile_error(ty.span(), "unsupported type in this position")
}
}
for arg in sig.inputs.iter_mut() { iflet FnArg::Typed(pt) = arg {
add_to_type(&mut sig.generics, &pt.pat, &mut pt.ty)
}
}
}
/// Generate a #[derive(Debug)] Attribute fn derive_debug() -> Attribute { let ml = parse2(quote!(derive(Debug))).unwrap();
Attribute {
pound_token: <Token![#]>::default(),
style: AttrStyle::Outer,
bracket_token: token::Bracket::default(),
meta: Meta::List(ml)
}
}
/// Add "Mock" to the front of the named type fn mock_ident_in_type(ty: &mutType) { match ty { Type::Path(type_path) => { if type_path.path.segments.len() != 1 {
compile_error(type_path.path.span(), "mockall_derive only supports structs defined in the current module"); return;
} let ident = &mut type_path.path.segments.last_mut().unwrap().ident;
*ident = gen_mock_ident(ident)
},
x => {
compile_error(x.span(), "mockall_derive only supports mocking traits and structs");
}
};
}
/// Performs transformations on the ItemImpl to make it mockable fn mockable_item_impl(mut impl_: ItemImpl, name: &Ident, generics: &Generics)
-> ItemImpl
{
mock_ident_in_type(&mut impl_.self_ty); for item in impl_.items.iter_mut() { iflet ImplItem::Fn(refmut iim) = item {
mockable_method(iim, name, generics);
}
}
impl_
}
/// Performs transformations on the method to make it mockable fn mockable_method(meth: &mut ImplItemFn, 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);
dewhereselfify(&mut meth.sig.generics); iflet ReturnType::Type(_, ty) = &mut meth.sig.output {
deselfify(ty, name, generics);
deanonymize(ty);
}
sanity_check_sig(&meth.sig);
}
/// Performs transformations on the method to make it mockable fn mockable_trait_method(
meth: &mut TraitItemFn,
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);
dewhereselfify(&mut meth.sig.generics); iflet ReturnType::Type(_, ty) = &mut meth.sig.output {
deselfify(ty, name, generics);
deanonymize(ty);
}
sanity_check_sig(&meth.sig);
}
fn sanity_check_sig(sig: &Signature) { for arg in sig.inputs.iter() { iflet FnArg::Typed(pt) = arg { ifletType::ImplTrait(it) = pt.ty.as_ref() { let bounds = &it.bounds; let s = format!( "Mockall does not support \"impltrait\" in argument position. Use \"T: {}\" instead",
quote!(#bounds)
);
compile_error(it.span(), &s);
}
}
}
}
/// Converts a TraitItemConst into an ImplItemConst fn tic2iic(tic: TraitItemConst, vis: &syn::Visibility) -> ImplItemConst { let span = tic.span(); let (eq_token, expr) = tic.default.unwrap_or_else(|| {
compile_error(span, "Mocked associated consts must have a default implementation");
(<Token![=]>::default(), Expr::Verbatim(TokenStream::new()))
});
ImplItemConst {
attrs: tic.attrs,
vis: vis.clone(),
defaultness: None,
const_token: tic.const_token,
generics: tic.generics,
ident: tic.ident,
colon_token: tic.colon_token,
ty: tic.ty,
eq_token,
expr,
semi_token: tic.semi_token
}
}
/// Converts a TraitItemFn into an ImplItemFn fn tif2iif(m: syn::TraitItemFn, vis: &syn::Visibility)
-> syn::ImplItemFn
{ let empty_block = Block {
brace_token: token::Brace::default(),
stmts: Vec::new()
};
syn::ImplItemFn{
attrs: m.attrs,
vis: vis.clone(),
defaultness: None,
sig: m.sig,
block: empty_block
}
}
/// Converts a TraitItemType into an ImplItemType fn tit2iit(tit: TraitItemType, vis: &Visibility) -> ImplItemType { let span = tit.span(); let (eq_token, ty) = tit.default.unwrap_or_else(|| {
compile_error(span, "associated types in mock! must be fully specified");
(token::Eq::default(), Type::Verbatim(TokenStream::new()))
});
ImplItemType {
attrs: tit.attrs,
vis: vis.clone(),
defaultness: None,
type_token: tit.type_token,
ident: tit.ident,
generics: tit.generics,
eq_token,
ty,
semi_token: tit.semi_token,
}
}
/// Like a TraitItemFn, but with a visibility struct TraitItemVFn { pub vis: Visibility, pub tif: TraitItemFn
}
impl MockableStruct { /// Does this struct derive Debug? pubfn derives_debug(&self) -> bool { self.attrs.iter()
.any(|attr|{ letmut derive_debug = false; if attr.path().is_ident("derive") {
attr.parse_nested_meta(|meta| { if meta.path.is_ident("Debug") {
derive_debug = true;
}
Ok(())
}).unwrap();
}
derive_debug
})
}
}
impl From<(Attrs, ItemTrait)> for MockableStruct { fn from((attrs, item_trait): (Attrs, ItemTrait)) -> MockableStruct { let trait_ = attrs.substitute_trait(&item_trait); letmut attrs = trait_.attrs.clone();
attrs.push(derive_debug()); let vis = trait_.vis.clone(); let name = gen_mock_ident(&trait_.ident); let generics = trait_.generics.clone(); let impls = vec![mockable_trait(trait_, &name, &generics)];
MockableStruct {
attrs,
consts: Vec::new(),
vis,
name,
generics,
methods: Vec::new(),
impls
}
}
}
impl From<ItemImpl> for MockableStruct { fn from(mut item_impl: ItemImpl) -> MockableStruct { let name = match &*item_impl.self_ty { Type::Path(type_path) => { let n = find_ident_from_path(&type_path.path).0;
gen_mock_ident(&n)
},
x => {
compile_error(x.span(), "mockall_derive only supports mocking traits and structs");
Ident::new("", Span::call_site())
}
}; letmut attrs = item_impl.attrs.clone();
attrs.push(derive_debug()); letmut consts = Vec::new(); let generics = item_impl.generics.clone(); letmut methods = Vec::new(); let vis = Visibility::Public(Token)); letmut impls = Vec::new(); iflet Some((bang, _path, _)) = &item_impl.trait_ { if bang.is_some() {
compile_error(bang.span(), "Unsupported by automock");
}
// Substitute any associated types in this ItemImpl. // NB: this would not be necessary if the user always fully // qualified them, e.g. `<Self as MyTrait>::MyType` letmut attrs = Attrs::default(); for item in item_impl.items.iter() { match item {
ImplItem::Const(_iic) =>
(),
ImplItem::Fn(_meth) =>
(),
ImplItem::Type(ty) => {
attrs.attrs.insert(ty.ident.clone(), ty.ty.clone());
},
x => compile_error(x.span(), "Unsupported by automock")
}
}
attrs.substitute_item_impl(&mut item_impl);
impls.push(mockable_item_impl(item_impl, &name, &generics));
} else { for item in item_impl.items.into_iter() { match item {
ImplItem::Fn(mut meth) => {
mockable_method(&mut meth, &name, &item_impl.generics);
methods.push(meth)
},
ImplItem::Const(iic) => consts.push(iic), // Rust doesn't allow types in an inherent impl
x => compile_error(x.span(), "Unsupported by Mockall in this context"),
}
}
};
MockableStruct {
attrs,
consts,
generics,
methods,
name,
vis,
impls,
}
}
}
impl Parse for MockableStruct { fn parse(input: ParseStream) -> syn::parse::Result<Self> { let attrs = input.call(syn::Attribute::parse_outer)?; let vis: syn::Visibility = input.parse()?; let original_name: syn::Ident = input.parse()?; letmut generics: syn::Generics = input.parse()?; let wc: Option<syn::WhereClause> = input.parse()?;
generics.where_clause = wc; let name = gen_mock_ident(&original_name); let impl_content; let _brace_token = braced!(impl_content in input); letmut consts = Vec::new(); letmut methods = Vec::new(); while !impl_content.is_empty() { let item: ImplItem = impl_content.parse()?; match item {
ImplItem::Verbatim(ts) => { let tivf: TraitItemVFn = parse2(ts)?; letmut iim = tif2iif(tivf.tif, &tivf.vis);
mockable_method(&mut iim, &name, &generics);
methods.push(iim);
}
ImplItem::Const(iic) => consts.push(iic),
_ => { return Err(input.error("Unsupported in this context"));
}
}
}
letmut impls = Vec::new(); while !input.is_empty() { let item: Item = input.parse()?; match item {
Item::Impl(mut ii) => { for item in ii.items.iter_mut() { // Convert any methods that syn couldn't parse as // ImplItemFn. iflet ImplItem::Verbatim(ts) = item { let tif: TraitItemFn = parse2(ts.clone()).unwrap(); let iim = tif2iif(tif, &Visibility::Inherited);
*item = ImplItem::Fn(iim);
}
}
impls.push(mockable_item_impl(ii, &name, &generics));
}
_ => return Err(input.error("Unsupported in this context")),
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.