mockall_derive-0.13.0/.cargo_vcs_info.json0000644000000001540000000000100140750ustar { "git": { "sha1": "63a6b76054a160c51554839097315b98c788f3fc" }, "path_in_vcs": "mockall_derive" }mockall_derive-0.13.0/Cargo.toml0000644000000026130000000000100120750ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "mockall_derive" version = "0.13.0" authors = ["Alan Somers "] build = "build.rs" autobins = false autoexamples = false autotests = false autobenches = false description = """ Procedural macros for Mockall """ documentation = "https://docs.rs/mockall_derive" readme = "README.md" keywords = [ "mock", "mocking", "testing", ] categories = ["development-tools::testing"] license = "MIT OR Apache-2.0" repository = "https://github.com/asomers/mockall" [package.metadata.release] push = false tag = false [lib] name = "mockall_derive" path = "src/lib.rs" proc-macro = true [dependencies.cfg-if] version = "1.0" [dependencies.proc-macro2] version = "1.0.60" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0.9" features = [ "extra-traits", "full", ] [dev-dependencies.pretty_assertions] version = "1.3" [features] nightly_derive = ["proc-macro2/nightly"] mockall_derive-0.13.0/Cargo.toml.orig000064400000000000000000000016301046102023000155540ustar 00000000000000[package] name = "mockall_derive" version = "0.13.0" authors = ["Alan Somers "] license = "MIT OR Apache-2.0" repository = "https://github.com/asomers/mockall" categories = ["development-tools::testing"] keywords = ["mock", "mocking", "testing"] documentation = "https://docs.rs/mockall_derive" edition = "2021" description = """ Procedural macros for Mockall """ # Skip tagging and pushing. Those will be done by the mockall crate [package.metadata.release] tag = false push = false [lib] proc-macro = true [features] # Extra features for the nightly compiler only # Must have a different name than the corresponding feature in mockall in order # to catch hygiene violations. nightly_derive = ["proc-macro2/nightly"] [dependencies] cfg-if = "1.0" proc-macro2 = "1.0.60" quote = "1.0" syn = { version = "2.0.9", features = ["extra-traits", "full"] } [dev-dependencies] pretty_assertions = "1.3" mockall_derive-0.13.0/LICENSE-APACHE000064400000000000000000000251371046102023000146210ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. mockall_derive-0.13.0/LICENSE-MIT000064400000000000000000000020371046102023000143230ustar 00000000000000Copyright (c) 2019 Alan Somers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mockall_derive-0.13.0/README.md000064400000000000000000000005471046102023000141520ustar 00000000000000# Mockall_derive This crate should never be used directly. You should use [`mockall`](https://crates.io/crates/mockall) instead. # License `mockall` is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses. See LICENSE-APACHE, and LICENSE-MIT for details mockall_derive-0.13.0/build.rs000064400000000000000000000003361046102023000143340ustar 00000000000000fn main() { // Avoid unnecessary re-building. println!("cargo:rerun-if-changed=build.rs"); // Quiet rustc's helpful lint. reprocheck is defined in CI. println!("cargo:rustc-check-cfg=cfg(reprocheck)"); } mockall_derive-0.13.0/src/automock.rs000064400000000000000000000270001046102023000156430ustar 00000000000000// vim: tw=80 use super::*; use std::collections::HashMap; use syn::parse::{Parse, ParseStream}; /// A single automock attribute // This enum is very short-lived, so it's fine not to box it. #[allow(clippy::large_enum_variant)] enum Attr { Type(TraitItemType), } impl Parse for Attr { fn parse(input: ParseStream) -> parse::Result { let lookahead = input.lookahead1(); if lookahead.peek(Token![type]) { input.parse().map(Attr::Type) } else { Err(lookahead.error()) } } } /// automock attributes #[derive(Debug, Default)] pub(crate) struct Attrs { pub attrs: HashMap, } impl Attrs { fn get_path(&self, path: &Path) -> Option { if path.leading_colon.is_none() & (path.segments.len() == 2) { if path.segments.first().unwrap().ident == "Self" { let ident = &path.segments.last().unwrap().ident; self.attrs.get(ident).cloned() } else { None } } else { None } } pub(crate) fn substitute_item_impl(&self, item_impl: &mut ItemImpl) { let (_, trait_path, _) = item_impl.trait_.as_ref() .expect("Should only be called for trait item impls"); let trait_ident = find_ident_from_path(trait_path).0; for item in item_impl.items.iter_mut() { if let ImplItem::Fn(method) = item { let sig = &mut method.sig; for fn_arg in sig.inputs.iter_mut() { if let FnArg::Typed(arg) = fn_arg { self.substitute_type(&mut arg.ty, &trait_ident); } } if let ReturnType::Type(_, ref mut ty) = &mut sig.output { self.substitute_type(ty, &trait_ident); } } } } fn substitute_path_segment(&self, seg: &mut PathSegment, traitname: &Ident) { match &mut seg.arguments { PathArguments::None => /* nothing to do */(), PathArguments::Parenthesized(p) => { compile_error(p.span(), "Mockall does not support mocking Fn objects. See https://github.com/asomers/mockall/issues/139"); }, PathArguments::AngleBracketed(abga) => { for arg in abga.args.iter_mut() { match arg { GenericArgument::Lifetime(_) => { /* * Nothing to do, as long as lifetimes can't be * associated types */ } GenericArgument::Type(ty) => { self.substitute_type(ty, traitname) }, GenericArgument::AssocConst(_) => { // Nothing to do } GenericArgument::AssocType(at) => { self.substitute_type(&mut at.ty, traitname); } // TODO: Constraints _ => { // Not handled. Hopefully doing nothing works. } } } }, } } /// Recursively substitute types in the input fn substitute_type(&self, ty: &mut Type, traitname: &Ident) { match ty { Type::Slice(s) => { self.substitute_type(s.elem.as_mut(), traitname) }, Type::Array(a) => { self.substitute_type(a.elem.as_mut(), traitname) }, Type::Ptr(p) => { self.substitute_type(p.elem.as_mut(), traitname) }, Type::Reference(r) => { self.substitute_type(r.elem.as_mut(), traitname) }, Type::BareFn(bfn) => { for fn_arg in bfn.inputs.iter_mut() { self.substitute_type(&mut fn_arg.ty, traitname); } if let ReturnType::Type(_, ref mut ty) = &mut bfn.output { self.substitute_type(ty, traitname); } }, Type::Tuple(tuple) => { for elem in tuple.elems.iter_mut() { self.substitute_type(elem, traitname) } } Type::Path(path) => { if let Some(ref qself) = path.qself { let qp = if let Type::Path(p) = qself.ty.as_ref() { &p.path } else { panic!("QSelf's type isn't a path?") }; let qident = &qp.segments.first().unwrap().ident; if qself.position != 1 || qp.segments.len() != 1 || path.path.segments.len() != 2 || qident != "Self" { compile_error(path.span(), "QSelf is a work in progress"); } let mut seg_iter = path.path.segments.iter().rev(); let last_seg = seg_iter.next().unwrap(); let to_sub = &last_seg.ident; let penultimate_seg = seg_iter.next().unwrap(); let qident = &penultimate_seg.ident; drop(seg_iter); if qident != traitname { compile_error(qident.span(), "Mockall does not support QSelf substitutions except for the trait being mocked"); } if let Some(new_type) = self.attrs.get(to_sub) { *ty = new_type.clone(); } else { compile_error(to_sub.span(), "Unknown type substitution for QSelf"); } } else if let Some(newty) = self.get_path(&path.path) { *ty = newty; } else { for seg in path.path.segments.iter_mut() { self.substitute_path_segment(seg, traitname); } } }, Type::TraitObject(to) => { for bound in to.bounds.iter_mut() { self.substitute_type_param_bound(bound, traitname); } }, Type::ImplTrait(it) => { for bound in it.bounds.iter_mut() { self.substitute_type_param_bound(bound, traitname); } }, Type::Paren(p) => { self.substitute_type(p.elem.as_mut(), traitname) }, Type::Group(g) => { self.substitute_type(g.elem.as_mut(), traitname) }, Type::Macro(_) | Type::Verbatim(_) => { compile_error(ty.span(), "mockall_derive does not support this type when using associated types"); }, Type::Infer(_) | Type::Never(_) => { /* Nothing to do */ }, _ => compile_error(ty.span(), "Unsupported type"), } } fn substitute_type_param_bound(&self, bound: &mut TypeParamBound, traitname: &Ident) { if let TypeParamBound::Trait(t) = bound { match self.get_path(&t.path) { None => { for seg in t.path.segments.iter_mut() { self.substitute_path_segment(seg, traitname); } }, Some(Type::Path(type_path)) => { t.path = type_path.path; }, Some(_) => { compile_error(t.path.span(), "Can only substitute paths for trait bounds"); } } } } pub(crate) fn substitute_trait(&self, item: &ItemTrait) -> ItemTrait { let mut output = item.clone(); for trait_item in output.items.iter_mut() { match trait_item { TraitItem::Type(tity) => { if let Some(ty) = self.attrs.get(&tity.ident) { let span = tity.span(); tity.default = Some((Token![=](span), ty.clone())); // Concrete associated types aren't allowed to have // bounds tity.bounds = Punctuated::new(); } else { compile_error(tity.span(), "Default value not given for associated type"); } }, TraitItem::Fn(method) => { let sig = &mut method.sig; for fn_arg in sig.inputs.iter_mut() { if let FnArg::Typed(arg) = fn_arg { self.substitute_type(&mut arg.ty, &item.ident); } } if let ReturnType::Type(_, ref mut ty) = &mut sig.output { self.substitute_type(ty, &item.ident); } }, _ => { // Nothing to do } } } output } } impl Parse for Attrs { fn parse(input: ParseStream) -> parse::Result { let mut attrs = HashMap::new(); while !input.is_empty() { let attr: Attr = input.parse()?; match attr { Attr::Type(trait_item_type) => { let ident = trait_item_type.ident.clone(); if let Some((_, ty)) = trait_item_type.default { attrs.insert(ident, ty.clone()); } else { compile_error(trait_item_type.span(), "automock type attributes must have a default value"); } } } } Ok(Attrs{attrs}) } } /// Unit tests for `Attrs`. #[cfg(test)] mod t { use super::super::*; use pretty_assertions::assert_eq; fn check_substitute_type( attrs: TokenStream, input: TokenStream, traitname: Ident, expected: TokenStream) { let _self: super::Attrs = parse2(attrs).unwrap(); let mut in_ty: Type = parse2(input).unwrap(); let expect_ty: Type = parse2(expected).unwrap(); _self.substitute_type(&mut in_ty, &traitname); assert_eq!(in_ty, expect_ty); } #[test] fn qself() { check_substitute_type(quote!(type T = u32;), quote!(::T), format_ident!("Foo"), quote!(u32)); } #[test] #[should_panic(expected = "Mockall does not support QSelf substitutions except for the trait being mocked")] fn qself_other() { check_substitute_type(quote!(type T = u32;), quote!(::T), format_ident!("Foo"), quote!(u32)); } #[test] #[should_panic(expected = "Unknown type substitution for QSelf")] fn unknown_substitution() { check_substitute_type(quote!(type T = u32;), quote!(::Q), format_ident!("Foo"), quote!(u32)); } } mockall_derive-0.13.0/src/lib.rs000064400000000000000000002207101046102023000145720ustar 00000000000000// vim: tw=80 //! Proc Macros for use with Mockall //! //! You probably don't want to use this crate directly. Instead, you should use //! its reexports via the [`mockall`](https://docs.rs/mockall/latest/mockall) //! crate. #![cfg_attr(feature = "nightly_derive", feature(proc_macro_diagnostic))] #![cfg_attr(test, deny(warnings))] use cfg_if::cfg_if; use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, format_ident, quote}; use std::{ env, hash::BuildHasherDefault }; use syn::{ *, punctuated::Punctuated, spanned::Spanned }; mod automock; mod mock_function; mod mock_item; mod mock_item_struct; mod mock_trait; mod mockable_item; mod mockable_struct; use crate::automock::Attrs; use crate::mockable_struct::MockableStruct; use crate::mock_item::MockItem; use crate::mock_item_struct::MockItemStruct; use crate::mockable_item::MockableItem; // Define deterministic aliases for these common types. type HashMap = std::collections::HashMap>; type HashSet = std::collections::HashSet>; cfg_if! { // proc-macro2's Span::unstable method requires the nightly feature, and it // doesn't work in test mode. // https://github.com/alexcrichton/proc-macro2/issues/159 if #[cfg(all(feature = "nightly_derive", not(test)))] { fn compile_error(span: Span, msg: &str) { span.unstable() .error(msg) .emit(); } } else { fn compile_error(_span: Span, msg: &str) { panic!("{msg}. More information may be available when mockall is built with the \"nightly\" feature."); } } } /// Does this Attribute represent Mockall's "concretize" pseudo-attribute? fn is_concretize(attr: &Attribute) -> bool { if attr.path().segments.last().unwrap().ident == "concretize" { true } else if attr.path().is_ident("cfg_attr") { match &attr.meta { Meta::List(ml) => { ml.tokens.to_string().contains("concretize") }, // cfg_attr should always contain a list _ => false, } } else { false } } /// replace generic arguments with concrete trait object arguments /// /// # Return /// /// * A Generics object with the concretized types removed /// * An array of transformed argument types, suitable for matchers and /// returners /// * An array of expressions that should be passed to the `call` function. fn concretize_args(gen: &Generics, sig: &Signature) -> (Generics, Punctuated, Vec, Signature) { let args = &sig.inputs; let mut hm = HashMap::default(); let mut needs_muts = HashMap::default(); let mut save_types = |ident: &Ident, tpb: &Punctuated| { if !tpb.is_empty() { let mut pat = quote!(&(dyn #tpb)); let mut needs_mut = false; if let Some(TypeParamBound::Trait(t)) = tpb.first() { if t.path.segments.first().map(|seg| &seg.ident == "FnMut") .unwrap_or(false) { // For FnMut arguments, the rfunc needs a mutable reference pat = quote!(&mut (dyn #tpb)); needs_mut = true; } } if let Ok(newty) = parse2::(pat) { // substitute T arguments let subst_ty: Type = parse2(quote!(#ident)).unwrap(); needs_muts.insert(subst_ty.clone(), needs_mut); hm.insert(subst_ty, (newty.clone(), None)); // substitute &T arguments let subst_ty: Type = parse2(quote!(&#ident)).unwrap(); needs_muts.insert(subst_ty.clone(), needs_mut); hm.insert(subst_ty, (newty, None)); } else { compile_error(tpb.span(), "Type cannot be made into a trait object"); } if let Ok(newty) = parse2::(quote!(&mut (dyn #tpb))) { // substitute &mut T arguments let subst_ty: Type = parse2(quote!(&mut #ident)).unwrap(); needs_muts.insert(subst_ty.clone(), needs_mut); hm.insert(subst_ty, (newty, None)); } else { compile_error(tpb.span(), "Type cannot be made into a trait object"); } // I wish we could substitute &[T] arguments. But there's no way // for the mock method to turn &[T] into &[&dyn T]. if let Ok(newty) = parse2::(quote!(&[&(dyn #tpb)])) { let subst_ty: Type = parse2(quote!(&[#ident])).unwrap(); needs_muts.insert(subst_ty.clone(), needs_mut); hm.insert(subst_ty, (newty, Some(tpb.clone()))); } else { compile_error(tpb.span(), "Type cannot be made into a trait object"); } } }; for g in gen.params.iter() { if let GenericParam::Type(tp) = g { save_types(&tp.ident, &tp.bounds); // else there had better be a where clause } } if let Some(wc) = &gen.where_clause { for pred in wc.predicates.iter() { if let WherePredicate::Type(pt) = pred { let bounded_ty = &pt.bounded_ty; if let Ok(ident) = parse2::(quote!(#bounded_ty)) { save_types(&ident, &pt.bounds); } else { // We can't yet handle where clauses this complicated } } } } let outg = Generics { lt_token: None, gt_token: None, params: Punctuated::new(), where_clause: None }; let outargs = args.iter().map(|arg| { if let FnArg::Typed(pt) = arg { let mut call_pt = pt.clone(); demutify_arg(&mut call_pt); if let Some((newty, _)) = hm.get(&pt.ty) { FnArg::Typed(PatType { attrs: Vec::default(), pat: call_pt.pat, colon_token: pt.colon_token, ty: Box::new(newty.clone()) }) } else { FnArg::Typed(PatType { attrs: Vec::default(), pat: call_pt.pat, colon_token: pt.colon_token, ty: pt.ty.clone() }) } } else { arg.clone() } }).collect(); // Finally, Reference any concretizing arguments // use filter_map to remove the &self argument let call_exprs = args.iter().filter_map(|arg| { match arg { FnArg::Typed(pt) => { let mut pt2 = pt.clone(); demutify_arg(&mut pt2); let pat = &pt2.pat; if pat_is_self(pat) { None } else if let Some((_, newbound)) = hm.get(&pt.ty) { if let Type::Reference(tr) = &*pt.ty { if let Type::Slice(_ts) = &*tr.elem { // Assume _ts is the generic type or we wouldn't be // here Some(quote!( &(0..#pat.len()) .map(|__mockall_i| &#pat[__mockall_i] as &(dyn #newbound)) .collect::>() )) } else { Some(quote!(#pat)) } } else if needs_muts.get(&pt.ty).cloned().unwrap_or(false) { Some(quote!(&mut #pat)) } else { Some(quote!(&#pat)) } } else { Some(quote!(#pat)) } }, FnArg::Receiver(_) => None, } }).collect(); // Add any necessary "mut" qualifiers to the Signature let mut altsig = sig.clone(); for arg in altsig.inputs.iter_mut() { if let FnArg::Typed(pt) = arg { if needs_muts.get(&pt.ty).cloned().unwrap_or(false) { if let Pat::Ident(pi) = &mut *pt.pat { pi.mutability = Some(Token![mut](pi.mutability.span())); } else { compile_error(pt.pat.span(), "This Pat type is not yet supported by Mockall when used as an argument to a concretized function.") } } } } (outg, outargs, call_exprs, altsig) } fn deanonymize_lifetime(lt: &mut Lifetime) { if lt.ident == "_" { lt.ident = format_ident!("static"); } } fn deanonymize_path(path: &mut Path) { for seg in path.segments.iter_mut() { match &mut seg.arguments { PathArguments::None => (), PathArguments::AngleBracketed(abga) => { for ga in abga.args.iter_mut() { if let GenericArgument::Lifetime(lt) = ga { deanonymize_lifetime(lt) } } }, _ => compile_error(seg.arguments.span(), "Methods returning functions are TODO"), } } } /// Replace any references to the anonymous lifetime `'_` with `'static`. fn deanonymize(literal_type: &mut Type) { match literal_type { Type::Array(ta) => deanonymize(ta.elem.as_mut()), Type::BareFn(tbf) => { if let ReturnType::Type(_, ref mut bt) = tbf.output { deanonymize(bt.as_mut()); } for input in tbf.inputs.iter_mut() { deanonymize(&mut input.ty); } }, Type::Group(tg) => deanonymize(tg.elem.as_mut()), Type::Infer(_) => (), Type::Never(_) => (), Type::Paren(tp) => deanonymize(tp.elem.as_mut()), Type::Path(tp) => { if let Some(ref mut qself) = tp.qself { deanonymize(qself.ty.as_mut()); } deanonymize_path(&mut tp.path); }, Type::Ptr(tptr) => deanonymize(tptr.elem.as_mut()), Type::Reference(tr) => { if let Some(lt) = tr.lifetime.as_mut() { deanonymize_lifetime(lt) } deanonymize(tr.elem.as_mut()); }, Type::Slice(s) => deanonymize(s.elem.as_mut()), Type::TraitObject(tto) => { for tpb in tto.bounds.iter_mut() { match tpb { TypeParamBound::Trait(tb) => deanonymize_path(&mut tb.path), TypeParamBound::Lifetime(lt) => deanonymize_lifetime(lt), _ => () } } }, Type::Tuple(tt) => { for ty in tt.elems.iter_mut() { deanonymize(ty) } } x => compile_error(x.span(), "Unimplemented type for deanonymize") } } // If there are any closures in the argument list, turn them into boxed // functions fn declosurefy(gen: &Generics, args: &Punctuated) -> (Generics, Punctuated, Vec) { let mut hm = HashMap::default(); let mut save_fn_types = |ident: &Ident, tpb: &TypeParamBound| { if let TypeParamBound::Trait(tb) = tpb { let fident = &tb.path.segments.last().unwrap().ident; if ["Fn", "FnMut", "FnOnce"].iter().any(|s| fident == *s) { let newty: Type = parse2(quote!(Box)).unwrap(); let subst_ty: Type = parse2(quote!(#ident)).unwrap(); assert!(hm.insert(subst_ty, newty).is_none(), "A generic parameter had two Fn bounds?"); } } }; // First, build a HashMap of all Fn generic types for g in gen.params.iter() { if let GenericParam::Type(tp) = g { for tpb in tp.bounds.iter() { save_fn_types(&tp.ident, tpb); } } } if let Some(wc) = &gen.where_clause { for pred in wc.predicates.iter() { if let WherePredicate::Type(pt) = pred { let bounded_ty = &pt.bounded_ty; if let Ok(ident) = parse2::(quote!(#bounded_ty)) { for tpb in pt.bounds.iter() { save_fn_types(&ident, tpb); } } else { // We can't yet handle where clauses this complicated } } } } // Then remove those types from both the Generics' params and where clause let should_remove = |ident: &Ident| { let ty: Type = parse2(quote!(#ident)).unwrap(); hm.contains_key(&ty) }; let params = gen.params.iter() .filter(|g| { if let GenericParam::Type(tp) = g { !should_remove(&tp.ident) } else { true } }).cloned() .collect::>(); let mut wc2 = gen.where_clause.clone(); if let Some(wc) = &mut wc2 { wc.predicates = wc.predicates.iter() .filter(|wp| { if let WherePredicate::Type(pt) = wp { let bounded_ty = &pt.bounded_ty; if let Ok(ident) = parse2::(quote!(#bounded_ty)) { !should_remove(&ident) } else { // We can't yet handle where clauses this complicated true } } else { true } }).cloned() .collect::>(); if wc.predicates.is_empty() { wc2 = None; } } let outg = Generics { lt_token: if params.is_empty() { None } else { gen.lt_token }, gt_token: if params.is_empty() { None } else { gen.gt_token }, params, where_clause: wc2 }; // Next substitute Box into the arguments let outargs = args.iter().map(|arg| { if let FnArg::Typed(pt) = arg { let mut immutable_pt = pt.clone(); demutify_arg(&mut immutable_pt); if let Some(newty) = hm.get(&pt.ty) { FnArg::Typed(PatType { attrs: Vec::default(), pat: immutable_pt.pat, colon_token: pt.colon_token, ty: Box::new(newty.clone()) }) } else { FnArg::Typed(PatType { attrs: Vec::default(), pat: immutable_pt.pat, colon_token: pt.colon_token, ty: pt.ty.clone() }) } } else { arg.clone() } }).collect(); // Finally, Box any closure arguments // use filter_map to remove the &self argument let callargs = args.iter().filter_map(|arg| { match arg { FnArg::Typed(pt) => { let mut pt2 = pt.clone(); demutify_arg(&mut pt2); let pat = &pt2.pat; if pat_is_self(pat) { None } else if hm.contains_key(&pt.ty) { Some(quote!(Box::new(#pat))) } else { Some(quote!(#pat)) } }, FnArg::Receiver(_) => None, } }).collect(); (outg, outargs, callargs) } /// Replace any "impl trait" types with "Box" or equivalent. fn deimplify(rt: &mut ReturnType) { if let ReturnType::Type(_, ty) = rt { if let Type::ImplTrait(ref tit) = &**ty { let needs_pin = tit.bounds .iter() .any(|tpb| { if let TypeParamBound::Trait(tb) = tpb { if let Some(seg) = tb.path.segments.last() { seg.ident == "Future" || seg.ident == "Stream" } else { // It might still be a Future, but we can't guess // what names it might be imported under. Too bad. false } } else { false } }); let bounds = &tit.bounds; if needs_pin { *ty = parse2(quote!(::std::pin::Pin>)).unwrap(); } else { *ty = parse2(quote!(Box)).unwrap(); } } } } /// 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() { match arg { FnArg::Receiver(r) => if r.reference.is_none() { r.mutability = None }, FnArg::Typed(pt) => demutify_arg(pt), } } } /// Remove any "mut" from a method argument's binding. fn demutify_arg(arg: &mut PatType) { match *arg.pat { Pat::Wild(_) => { compile_error(arg.span(), "Mocked methods must have named arguments"); }, Pat::Ident(ref mut pat_ident) => { if let Some(r) = &pat_ident.by_ref { compile_error(r.span(), "Mockall does not support by-reference argument bindings"); } if let Some((_at, subpat)) = &pat_ident.subpat { compile_error(subpat.span(), "Mockall does not support subpattern bindings"); } pat_ident.mutability = None; }, _ => { compile_error(arg.span(), "Unsupported argument type"); } }; } fn deselfify_path(path: &mut Path, actual: &Ident, generics: &Generics) { for seg in path.segments.iter_mut() { if seg.ident == "Self" { seg.ident = actual.clone(); if let PathArguments::None = seg.arguments { if !generics.params.is_empty() { let args = generics.params.iter() .map(|gp| { match gp { GenericParam::Type(tp) => { let ident = tp.ident.clone(); GenericArgument::Type( Type::Path( TypePath { qself: None, path: Path::from(ident) } ) ) }, GenericParam::Lifetime(ld) =>{ GenericArgument::Lifetime( ld.lifetime.clone() ) } _ => unimplemented!(), } }).collect::>(); seg.arguments = PathArguments::AngleBracketed( AngleBracketedGenericArguments { colon2_token: None, lt_token: generics.lt_token.unwrap(), args, gt_token: generics.gt_token.unwrap(), } ); } } else { compile_error(seg.arguments.span(), "Type arguments after Self are unexpected"); } } if let PathArguments::AngleBracketed(abga) = &mut seg.arguments { for arg in abga.args.iter_mut() { match arg { GenericArgument::Type(ty) => deselfify(ty, actual, generics), GenericArgument::AssocType(at) => deselfify(&mut at.ty, actual, generics), _ => /* Nothing to do */(), } } } } } /// Replace any references to `Self` in `literal_type` with `actual`. /// `generics` is the Generics field of the parent struct. Useful for /// constructor methods. fn deselfify(literal_type: &mut Type, actual: &Ident, generics: &Generics) { match literal_type { Type::Slice(s) => { deselfify(s.elem.as_mut(), actual, generics); }, Type::Array(a) => { deselfify(a.elem.as_mut(), actual, generics); }, Type::Ptr(p) => { deselfify(p.elem.as_mut(), actual, generics); }, Type::Reference(r) => { deselfify(r.elem.as_mut(), actual, generics); }, Type::Tuple(tuple) => { for elem in tuple.elems.iter_mut() { deselfify(elem, actual, generics); } } Type::Path(type_path) => { if let Some(ref mut qself) = type_path.qself { deselfify(qself.ty.as_mut(), actual, generics); } deselfify_path(&mut type_path.path, actual, generics); }, Type::Paren(p) => { deselfify(p.elem.as_mut(), actual, generics); }, Type::Group(g) => { deselfify(g.elem.as_mut(), actual, generics); }, Type::Macro(_) | Type::Verbatim(_) => { compile_error(literal_type.span(), "mockall_derive does not support this type as a return argument"); }, Type::TraitObject(tto) => { // Change types like `dyn Self` into `dyn MockXXX`. for bound in tto.bounds.iter_mut() { if let TypeParamBound::Trait(t) = bound { deselfify_path(&mut t.path, actual, generics); } } }, Type::ImplTrait(_) => { /* Should've already been flagged as a compile_error */ }, Type::BareFn(_) => { /* Bare functions can't have Self arguments. Nothing to do */ }, Type::Infer(_) | Type::Never(_) => { /* Nothing to do */ }, _ => compile_error(literal_type.span(), "Unsupported type"), } } /// Change any `Self` in a method's arguments' types with `actual`. /// `generics` is the Generics field of the parent struct. fn deselfify_args( args: &mut Punctuated, actual: &Ident, generics: &Generics) { for arg in args.iter_mut() { match arg { FnArg::Receiver(r) => { if r.colon_token.is_some() { deselfify(r.ty.as_mut(), actual, generics) } }, FnArg::Typed(pt) => deselfify(pt.ty.as_mut(), actual, generics) } } } fn find_ident_from_path(path: &Path) -> (Ident, PathArguments) { if path.segments.len() != 1 { compile_error(path.span(), "mockall_derive only supports structs defined in the current module"); return (Ident::new("", path.span()), PathArguments::None); } let last_seg = path.segments.last().unwrap(); (last_seg.ident.clone(), last_seg.arguments.clone()) } fn find_lifetimes_in_tpb(bound: &TypeParamBound) -> HashSet { let mut ret = HashSet::default(); match bound { TypeParamBound::Lifetime(lt) => { ret.insert(lt.clone()); }, TypeParamBound::Trait(tb) => { ret.extend(find_lifetimes_in_path(&tb.path)); }, _ => () }; ret } fn find_lifetimes_in_path(path: &Path) -> HashSet { let mut ret = HashSet::default(); for seg in path.segments.iter() { if let PathArguments::AngleBracketed(abga) = &seg.arguments { for arg in abga.args.iter() { match arg { GenericArgument::Lifetime(lt) => { ret.insert(lt.clone()); }, GenericArgument::Type(ty) => { ret.extend(find_lifetimes(ty)); }, GenericArgument::AssocType(at) => { ret.extend(find_lifetimes(&at.ty)); }, GenericArgument::Constraint(c) => { for bound in c.bounds.iter() { ret.extend(find_lifetimes_in_tpb(bound)); } }, GenericArgument::Const(_) => (), _ => () } } } } ret } fn find_lifetimes(ty: &Type) -> HashSet { match ty { Type::Array(ta) => find_lifetimes(ta.elem.as_ref()), Type::Group(tg) => find_lifetimes(tg.elem.as_ref()), Type::Infer(_ti) => HashSet::default(), Type::Never(_tn) => HashSet::default(), Type::Paren(tp) => find_lifetimes(tp.elem.as_ref()), Type::Path(tp) => { let mut ret = find_lifetimes_in_path(&tp.path); if let Some(qs) = &tp.qself { ret.extend(find_lifetimes(qs.ty.as_ref())); } ret }, Type::Ptr(tp) => find_lifetimes(tp.elem.as_ref()), Type::Reference(tr) => { let mut ret = find_lifetimes(tr.elem.as_ref()); if let Some(lt) = &tr.lifetime { ret.insert(lt.clone()); } ret }, Type::Slice(ts) => find_lifetimes(ts.elem.as_ref()), Type::TraitObject(tto) => { let mut ret = HashSet::default(); for bound in tto.bounds.iter() { ret.extend(find_lifetimes_in_tpb(bound)); } ret } Type::Tuple(tt) => { let mut ret = HashSet::default(); for ty in tt.elems.iter() { ret.extend(find_lifetimes(ty)); } ret }, Type::ImplTrait(tit) => { let mut ret = HashSet::default(); for tpb in tit.bounds.iter() { ret.extend(find_lifetimes_in_tpb(tpb)); } ret }, _ => { compile_error(ty.span(), "unsupported type in this context"); HashSet::default() } } } struct AttrFormatter<'a>{ attrs: &'a [Attribute], async_trait: bool, doc: bool, must_use: bool, } impl<'a> AttrFormatter<'a> { fn new(attrs: &'a [Attribute]) -> AttrFormatter<'a> { Self { attrs, async_trait: true, doc: true, must_use: false, } } fn async_trait(&mut self, allowed: bool) -> &mut Self { self.async_trait = allowed; self } fn doc(&mut self, allowed: bool) -> &mut Self { self.doc = allowed; self } fn must_use(&mut self, allowed: bool) -> &mut Self { self.must_use = allowed; self } // XXX This logic requires that attributes are imported with their // standard names. #[allow(clippy::needless_bool)] #[allow(clippy::if_same_then_else)] fn format(&mut self) -> Vec { self.attrs.iter() .filter(|attr| { let i = attr.path().segments.last().map(|ps| &ps.ident); if is_concretize(attr) { // Internally used attribute. Never emit. false } else if i.is_none() { false } else if *i.as_ref().unwrap() == "derive" { // We can't usefully derive any traits. Ignore them false } else if *i.as_ref().unwrap() == "doc" { self.doc } else if *i.as_ref().unwrap() == "async_trait" { self.async_trait } else if *i.as_ref().unwrap() == "inline" { // No need to inline mock functions. false } else if *i.as_ref().unwrap() == "cold" { // No need for such hints on mock functions. false } else if *i.as_ref().unwrap() == "instrument" { // We can't usefully instrument the mock method, so just // ignore this attribute. // https://docs.rs/tracing/0.1.23/tracing/attr.instrument.html false } else if *i.as_ref().unwrap() == "link_name" { // This shows up sometimes when mocking ffi functions. We // must not emit it on anything that isn't an ffi definition false } else if *i.as_ref().unwrap() == "must_use" { self.must_use } else if *i.as_ref().unwrap() == "auto_enum" { // Ignore auto_enum, because we transform the return value // into a trait object. false } else { true } }).cloned() .collect() } } /// Determine if this Pat is any kind of `self` binding fn pat_is_self(pat: &Pat) -> bool { if let Pat::Ident(pi) = pat { pi.ident == "self" } else { false } } /// 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 => (), PathArguments::AngleBracketed(ref mut abga) => { for arg in abga.args.iter_mut() { match arg { GenericArgument::Type(ref mut ty) => { *ty = supersuperfy(ty, levels); }, GenericArgument::AssocType(ref mut at) => { at.ty = supersuperfy(&at.ty, levels); }, GenericArgument::Constraint(ref mut constraint) => { supersuperfy_bounds(&mut constraint.bounds, levels); }, _ => (), } } }, PathArguments::Parenthesized(ref mut pga) => { for input in pga.inputs.iter_mut() { *input = supersuperfy(input, levels); } if let ReturnType::Type(_, ref mut ty) = pga.output { *ty = Box::new(supersuperfy(ty, levels)); } }, } } 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: usize) -> Type { let mut output = original.clone(); fn recurse(t: &mut Type, levels: usize) { match t { Type::Slice(s) => { recurse(s.elem.as_mut(), levels); }, Type::Array(a) => { recurse(a.elem.as_mut(), levels); }, Type::Ptr(p) => { recurse(p.elem.as_mut(), levels); }, Type::Reference(r) => { recurse(r.elem.as_mut(), levels); }, Type::BareFn(bfn) => { if let ReturnType::Type(_, ref mut bt) = bfn.output { recurse(bt.as_mut(), levels); } for input in bfn.inputs.iter_mut() { recurse(&mut input.ty, levels); } }, Type::Tuple(tuple) => { for elem in tuple.elems.iter_mut() { recurse(elem, levels); } } Type::Path(type_path) => { 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; } }, Type::Paren(p) => { recurse(p.elem.as_mut(), levels); }, Type::Group(g) => { recurse(g.elem.as_mut(), levels); }, Type::Macro(_) | Type::Verbatim(_) => { compile_error(t.span(), "mockall_derive does not support this type in this position"); }, Type::TraitObject(tto) => { for bound in tto.bounds.iter_mut() { if let TypeParamBound::Trait(tb) = bound { supersuperfy_path(&mut tb.path, levels); } } }, Type::ImplTrait(_) => { /* Should've already been flagged as a compile error */ }, Type::Infer(_) | Type::Never(_) => { /* Nothing to do */ }, _ => compile_error(t.span(), "Unsupported type"), } } recurse(&mut output, levels); output } 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); if let Some(ty) = tp.default.as_mut() { *ty = supersuperfy(ty, levels); } } } if let Some(wc) = generics.where_clause.as_mut() { for wp in wc.predicates.iter_mut() { if let WherePredicate::Type(pt) = wp { pt.bounded_ty = supersuperfy(&pt.bounded_ty, levels); supersuperfy_bounds(&mut pt.bounds, levels); } } } } fn supersuperfy_bounds( bounds: &mut Punctuated, levels: usize) { for bound in bounds.iter_mut() { if let TypeParamBound::Trait(tb) = bound { supersuperfy_path(&mut tb.path, levels); } } } /// Generate a suitable mockall::Key generic paramter from any Generics fn gen_keyid(g: &Generics) -> impl ToTokens { match g.params.len() { 0 => quote!(<()>), 1 => { let (_, tg, _) = g.split_for_impl(); quote!(#tg) }, _ => { // Rust doesn't support variadic Generics, so mockall::Key must // always have exactly one generic type. We need to add parentheses // around whatever type generics the caller passes. let tps = g.type_params() .map(|tp| tp.ident.clone()) .collect::>(); quote!(<(#tps)>) } } } /// Generate a mock identifier from the regular one: eg "Foo" => "MockFoo" fn gen_mock_ident(ident: &Ident) -> Ident { format_ident!("Mock{}", ident) } /// Generate an identifier for the mock struct's private module: eg "Foo" => /// "__mock_Foo" fn gen_mod_ident(struct_: &Ident, trait_: Option<&Ident>) -> Ident { if let Some(t) = trait_ { format_ident!("__mock_{struct_}_{}", t) } else { format_ident!("__mock_{struct_}") } } /// Combine two Generics structs, producing a new one that has the union of /// their parameters. fn merge_generics(x: &Generics, y: &Generics) -> Generics { /// Compare only the identifiers of two GenericParams fn cmp_gp_idents(x: &GenericParam, y: &GenericParam) -> bool { use GenericParam::*; match (x, y) { (Type(xtp), Type(ytp)) => xtp.ident == ytp.ident, (Lifetime(xld), Lifetime(yld)) => xld.lifetime == yld.lifetime, (Const(xc), Const(yc)) => xc.ident == yc.ident, _ => false } } /// Compare only the identifiers of two WherePredicates fn cmp_wp_idents(x: &WherePredicate, y: &WherePredicate) -> bool { use WherePredicate::*; match (x, y) { (Type(xpt), Type(ypt)) => xpt.bounded_ty == ypt.bounded_ty, (Lifetime(xpl), Lifetime(ypl)) => xpl.lifetime == ypl.lifetime, _ => false } } let mut out = if x.lt_token.is_none() && x.where_clause.is_none() { y.clone() } else if y.lt_token.is_none() && y.where_clause.is_none() { x.clone() } else { let mut out = x.clone(); // First merge the params 'outer_param: for yparam in y.params.iter() { // XXX: O(n^2) loop for outparam in out.params.iter_mut() { if cmp_gp_idents(outparam, yparam) { if let (GenericParam::Type(ref mut ot), GenericParam::Type(yt)) = (outparam, yparam) { ot.attrs.extend(yt.attrs.iter().cloned()); ot.colon_token = ot.colon_token.or(yt.colon_token); ot.eq_token = ot.eq_token.or(yt.eq_token); if ot.default.is_none() { ot.default.clone_from(&yt.default); } // XXX this might result in duplicate bounds if ot.bounds != yt.bounds { ot.bounds.extend(yt.bounds.iter().cloned()); } } continue 'outer_param; } } out.params.push(yparam.clone()); } out }; // Then merge the where clauses match (&mut out.where_clause, &y.where_clause) { (_, None) => (), (None, Some(wc)) => out.where_clause = Some(wc.clone()), (Some(out_wc), Some(y_wc)) => { 'outer_wc: for ypred in y_wc.predicates.iter() { // XXX: O(n^2) loop for outpred in out_wc.predicates.iter_mut() { if cmp_wp_idents(outpred, ypred) { if let (WherePredicate::Type(ref mut ot), WherePredicate::Type(yt)) = (outpred, ypred) { match (&mut ot.lifetimes, &yt.lifetimes) { (_, None) => (), (None, Some(bl)) => ot.lifetimes = Some(bl.clone()), (Some(obl), Some(ybl)) => // XXX: might result in duplicates obl.lifetimes.extend( ybl.lifetimes.iter().cloned()), }; // XXX: might result in duplicate bounds if ot.bounds != yt.bounds { ot.bounds.extend(yt.bounds.iter().cloned()) } } continue 'outer_wc; } } out_wc.predicates.push(ypred.clone()); } } } out } fn lifetimes_to_generic_params(lv: &Punctuated) -> Punctuated { lv.iter() .map(|lt| GenericParam::Lifetime(lt.clone())) .collect() } /// Transform a Vec of lifetimes into a Generics fn lifetimes_to_generics(lv: &Punctuated)-> Generics { if lv.is_empty() { Generics::default() } else { let params = lifetimes_to_generic_params(lv); Generics { lt_token: Some(Token![<](lv[0].span())), gt_token: Some(Token![>](lv[0].span())), params, where_clause: None } } } /// Split a generics list into three: one for type generics and where predicates /// that relate to the signature, one for lifetimes that relate to the arguments /// only, and one for lifetimes that relate to the return type only. fn split_lifetimes( generics: Generics, args: &Punctuated, rt: &ReturnType) -> (Generics, Punctuated, Punctuated) { if generics.lt_token.is_none() { return (generics, Default::default(), Default::default()); } // Check which types and lifetimes are referenced by the arguments let mut alts = HashSet::::default(); let mut rlts = HashSet::::default(); for arg in args { match arg { FnArg::Receiver(r) => { if let Some((_, Some(lt))) = &r.reference { alts.insert(lt.clone()); } }, FnArg::Typed(pt) => { alts.extend(find_lifetimes(pt.ty.as_ref())); }, }; }; if let ReturnType::Type(_, ty) = rt { rlts.extend(find_lifetimes(ty)); } let mut tv = Punctuated::new(); let mut alv = Punctuated::new(); let mut rlv = Punctuated::new(); for p in generics.params.into_iter() { match p { GenericParam::Lifetime(ltd) if rlts.contains(<d.lifetime) => rlv.push(ltd), GenericParam::Lifetime(ltd) if alts.contains(<d.lifetime) => alv.push(ltd), GenericParam::Lifetime(_) => { // Probably a lifetime parameter from the impl block that isn't // used by this particular method }, GenericParam::Type(_) => tv.push(p), _ => (), } } let tg = if tv.is_empty() { Generics::default() } else { Generics { lt_token: generics.lt_token, gt_token: generics.gt_token, params: tv, where_clause: generics.where_clause } }; (tg, alv, rlv) } /// Return the visibility that should be used for expectation!, given the /// original method's visibility. /// /// # Arguments /// - `vis`: Original visibility of the item /// - `levels`: How many modules will the mock item be nested in? fn expectation_visibility(vis: &Visibility, levels: usize) -> Visibility { if levels == 0 { return vis.clone(); } let in_token = Token![in](vis.span()); let super_token = Token![super](vis.span()); match vis { Visibility::Inherited => { // Private items need pub(in super::[...]) for each level let mut path = Path::from(super_token); for _ in 1..levels { path.segments.push(super_token.into()); } Visibility::Restricted(VisRestricted{ pub_token: Token![pub](vis.span()), paren_token: token::Paren::default(), in_token: Some(in_token), path: Box::new(path) }) }, Visibility::Restricted(vr) => { // crate => don't change // in crate::* => don't change // super => in super::super::super // self => in super::super // in anything_else => super::super::anything_else if vr.path.segments.first().unwrap().ident == "crate" { Visibility::Restricted(vr.clone()) } else { let mut out = vr.clone(); out.in_token = Some(in_token); for _ in 0..levels { out.path.segments.insert(0, super_token.into()); } Visibility::Restricted(out) } }, _ => vis.clone() } } fn staticize(generics: &Generics) -> Generics { let mut ret = generics.clone(); for lt in ret.lifetimes_mut() { lt.lifetime = Lifetime::new("'static", Span::call_site()); }; ret } fn mock_it>(inputs: M) -> TokenStream { let mockable: MockableItem = inputs.into(); let mock = MockItem::from(mockable); let ts = mock.into_token_stream(); if env::var("MOCKALL_DEBUG").is_ok() { println!("{ts}"); } ts } fn do_mock_once(input: TokenStream) -> TokenStream { let item: MockableStruct = match syn::parse2(input) { Ok(mock) => mock, Err(err) => { return err.to_compile_error(); } }; mock_it(item) } fn do_mock(input: TokenStream) -> TokenStream { cfg_if! { if #[cfg(reprocheck)] { let ts_a = do_mock_once(input.clone()); let ts_b = do_mock_once(input.clone()); assert_eq!(ts_a.to_string(), ts_b.to_string()); } } do_mock_once(input) } #[proc_macro_attribute] pub fn concretize( _attrs: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream { // Do nothing. This "attribute" is processed as text by the real proc // macros. input } #[proc_macro] pub fn mock(input: proc_macro::TokenStream) -> proc_macro::TokenStream { do_mock(input.into()).into() } #[proc_macro_attribute] pub fn automock(attrs: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream { let attrs: proc_macro2::TokenStream = attrs.into(); let input: proc_macro2::TokenStream = input.into(); do_automock(attrs, input).into() } fn do_automock_once(attrs: TokenStream, input: TokenStream) -> TokenStream { let mut output = input.clone(); let attrs: Attrs = match parse2(attrs) { Ok(a) => a, Err(err) => { return err.to_compile_error(); } }; let item: Item = match parse2(input) { Ok(item) => item, Err(err) => { return err.to_compile_error(); } }; output.extend(mock_it((attrs, item))); output } fn do_automock(attrs: TokenStream, input: TokenStream) -> TokenStream { cfg_if! { if #[cfg(reprocheck)] { let ts_a = do_automock_once(attrs.clone(), input.clone()); let ts_b = do_automock_once(attrs.clone(), input.clone()); assert_eq!(ts_a.to_string(), ts_b.to_string()); } } do_automock_once(attrs, input) } #[cfg(test)] 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 contains {:?}", &s); } /// Various tests for overall code generation that are hard or impossible to /// write as integration tests mod mock { use std::str::FromStr; use super::super::*; use super::*; #[test] fn inherent_method_visibility() { let code = " 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)); 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)); } #[test] fn must_use_struct() { let code = " #[must_use] pub Foo {} "; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let output = do_mock(ts).to_string(); assert_contains(&output, quote!(#[must_use] pub struct MockFoo)); } #[test] fn specific_impl() { let code = " pub Foo {} impl Bar for Foo { fn bar(&self); } impl Bar for Foo { fn bar(&self); } "; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let output = do_mock(ts).to_string(); assert_contains(&output, quote!(impl Bar for MockFoo)); assert_contains(&output, quote!(impl Bar for MockFoo)); // Ensure we don't duplicate the checkpoint function assert_not_contains(&output, quote!( self.Bar_expectations.checkpoint(); self.Bar_expectations.checkpoint(); )); // The expect methods should return specific types, not generic ones assert_contains(&output, quote!( pub fn expect_bar(&mut self) -> &mut __mock_MockFoo_Bar::__bar::Expectation )); assert_contains(&output, quote!( pub fn expect_bar(&mut self) -> &mut __mock_MockFoo_Bar::__bar::Expectation )); } } /// 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() { let code = " mod foo { /// Function docs pub fn bar() { unimplemented!() } } "; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); let output = do_automock(attrs_ts, ts).to_string(); assert_contains(&output, quote!(#[doc=" Function docs"] pub fn bar)); } #[test] fn method_visibility() { let code = " impl Foo { fn foo(&self) {} pub fn bar(&self) {} pub(super) fn baz(&self) {} pub(crate) fn bang(&self) {} pub(in super::x) fn bean(&self) {} }"; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); 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!(!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)); assert_contains(&output, quote!(pub(super) fn expect_baz)); assert_contains(&output, quote!(pub ( crate ) fn bang)); assert_contains(&output, quote!(pub ( crate ) fn expect_bang)); assert_contains(&output, quote!(pub ( in super :: x ) fn bean)); assert_contains(&output, quote!(pub ( in super :: x ) fn expect_bean)); } #[test] fn must_use_method() { let code = " impl Foo { #[must_use] fn foo(&self) -> i32 {42} }"; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); let output = do_automock(attrs_ts, ts).to_string(); assert_not_contains(&output, quote!(#[must_use] fn expect_foo)); assert_contains(&output, quote!(#[must_use] #[allow(dead_code)] fn foo)); } #[test] fn must_use_static_method() { let code = " impl Foo { #[must_use] fn foo() -> i32 {42} }"; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); let output = do_automock(attrs_ts, ts).to_string(); assert_not_contains(&output, quote!(#[must_use] fn expect)); assert_not_contains(&output, quote!(#[must_use] fn foo_context)); assert_contains(&output, quote!(#[must_use] #[allow(dead_code)] fn foo)); } #[test] fn must_use_trait() { let code = " #[must_use] trait Foo {} "; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); let output = do_automock(attrs_ts, ts).to_string(); assert_not_contains(&output, quote!(#[must_use] struct MockFoo)); } #[test] #[should_panic(expected = "can only mock inline modules")] fn external_module() { let code = "mod foo;"; let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); do_automock(attrs_ts, ts).to_string(); } #[test] fn trait_visibility() { let code = " pub(super) trait Foo {} "; let attrs_ts = proc_macro2::TokenStream::from_str("").unwrap(); let ts = proc_macro2::TokenStream::from_str(code).unwrap(); let output = do_automock(attrs_ts, ts).to_string(); assert_contains(&output, quote!(pub ( super ) struct MockFoo)); } } mod concretize_args { use super::*; #[allow(clippy::needless_range_loop)] // Clippy's suggestion is worse fn check_concretize( sig: TokenStream, expected_inputs: &[TokenStream], expected_call_exprs: &[TokenStream], expected_sig_inputs: &[TokenStream]) { let f: Signature = parse2(sig).unwrap(); let (generics, inputs, call_exprs, altsig) = concretize_args(&f.generics, &f); assert!(generics.params.is_empty()); assert_eq!(inputs.len(), expected_inputs.len()); assert_eq!(call_exprs.len(), expected_call_exprs.len()); for i in 0..inputs.len() { let actual = &inputs[i]; let exp = &expected_inputs[i]; assert_eq!(quote!(#actual).to_string(), quote!(#exp).to_string()); } for i in 0..call_exprs.len() { let actual = &call_exprs[i]; let exp = &expected_call_exprs[i]; assert_eq!(quote!(#actual).to_string(), quote!(#exp).to_string()); } for i in 0..altsig.inputs.len() { let actual = &altsig.inputs[i]; let exp = &expected_sig_inputs[i]; assert_eq!(quote!(#actual).to_string(), quote!(#exp).to_string()); } } #[test] fn bystanders() { check_concretize( quote!(fn foo>(x: i32, p: P, y: &f64)), &[quote!(x: i32), quote!(p: &(dyn AsRef)), quote!(y: &f64)], &[quote!(x), quote!(&p), quote!(y)], &[quote!(x: i32), quote!(p: P), quote!(y: &f64)] ); } #[test] fn function_args() { check_concretize( quote!(fn foo u32, F2: FnMut(&mut u32) -> u32, F3: FnOnce(u32) -> u32>(f1: F1, f2: F2, f3: F3)), &[quote!(f1: &(dyn Fn(u32) -> u32)), quote!(f2: &mut(dyn FnMut(&mut u32) -> u32)), quote!(f3: &(dyn FnOnce(u32) -> u32))], &[quote!(&f1), quote!(&mut f2), quote!(&f3)], &[quote!(f1: F1), quote!(mut f2: F2), quote!(f3: F3)] ); } #[test] fn multi_bounds() { check_concretize( quote!(fn foo + AsMut>(p: P)), &[quote!(p: &(dyn AsRef + AsMut))], &[quote!(&p)], &[quote!(p: P)], ); } #[test] fn mutable_reference_arg() { check_concretize( quote!(fn foo>(p: &mut P)), &[quote!(p: &mut (dyn AsMut))], &[quote!(p)], &[quote!(p: &mut P)], ); } #[test] fn mutable_reference_multi_bounds() { check_concretize( quote!(fn foo + AsMut>(p: &mut P)), &[quote!(p: &mut (dyn AsRef + AsMut))], &[quote!(p)], &[quote!(p: &mut P)] ); } #[test] fn reference_arg() { check_concretize( quote!(fn foo>(p: &P)), &[quote!(p: &(dyn AsRef))], &[quote!(p)], &[quote!(p: &P)] ); } #[test] fn simple() { check_concretize( quote!(fn foo>(p: P)), &[quote!(p: &(dyn AsRef))], &[quote!(&p)], &[quote!(p: P)], ); } #[test] fn slice() { check_concretize( quote!(fn foo>(p: &[P])), &[quote!(p: &[&(dyn AsRef)])], &[quote!(&(0..p.len()).map(|__mockall_i| &p[__mockall_i] as &(dyn AsRef)).collect::>())], &[quote!(p: &[P])] ); } #[test] fn slice_with_multi_bounds() { check_concretize( quote!(fn foo + AsMut>(p: &[P])), &[quote!(p: &[&(dyn AsRef + AsMut)])], &[quote!(&(0..p.len()).map(|__mockall_i| &p[__mockall_i] as &(dyn AsRef + AsMut)).collect::>())], &[quote!(p: &[P])] ); } #[test] fn where_clause() { check_concretize( quote!(fn foo

(p: P) where P: AsRef), &[quote!(p: &(dyn AsRef))], &[quote!(&p)], &[quote!(p: P)] ); } } mod declosurefy { use super::*; fn check_declosurefy( sig: TokenStream, expected_inputs: &[TokenStream], expected_call_exprs: &[TokenStream]) { let f: Signature = parse2(sig).unwrap(); let (generics, inputs, call_exprs) = declosurefy(&f.generics, &f.inputs); assert!(generics.params.is_empty()); assert_eq!(inputs.len(), expected_inputs.len()); assert_eq!(call_exprs.len(), expected_call_exprs.len()); for i in 0..inputs.len() { let actual = &inputs[i]; let exp = &expected_inputs[i]; assert_eq!(quote!(#actual).to_string(), quote!(#exp).to_string()); } for i in 0..call_exprs.len() { let actual = &call_exprs[i]; let exp = &expected_call_exprs[i]; assert_eq!(quote!(#actual).to_string(), quote!(#exp).to_string()); } } #[test] fn r#fn() { check_declosurefy( quote!(fn foo u32>(f: F)), &[quote!(f: Box u32>)], &[quote!(Box::new(f))] ); } #[test] fn fn_mut() { check_declosurefy( quote!(fn foo u32>(f: F)), &[quote!(f: Box u32>)], &[quote!(Box::new(f))] ); } #[test] fn fn_once() { check_declosurefy( quote!(fn foo u32>(f: F)), &[quote!(f: Box u32>)], &[quote!(Box::new(f))] ); } #[test] fn mutable_pattern() { check_declosurefy( quote!(fn foo u32>(mut f: F)), &[quote!(f: Box u32>)], &[quote!(Box::new(f))] ); } #[test] fn where_clause() { check_declosurefy( quote!(fn foo(f: F) where F: Fn(u32) -> u32), &[quote!(f: Box u32>)], &[quote!(Box::new(f))] ); } } mod deimplify { use super::*; fn check_deimplify(orig_ts: TokenStream, expected_ts: TokenStream) { let mut orig: ReturnType = parse2(orig_ts).unwrap(); let expected: ReturnType = parse2(expected_ts).unwrap(); deimplify(&mut orig); assert_eq!(quote!(#orig).to_string(), quote!(#expected).to_string()); } // Future is a special case #[test] fn impl_future() { check_deimplify( quote!(-> impl Future), quote!(-> ::std::pin::Pin>>) ); } // Future is a special case, wherever it appears #[test] fn impl_future_reverse() { check_deimplify( quote!(-> impl Send + Future), quote!(-> ::std::pin::Pin>>) ); } // Stream is a special case #[test] fn impl_stream() { check_deimplify( quote!(-> impl Stream), quote!(-> ::std::pin::Pin>>) ); } #[test] fn impl_trait() { check_deimplify( quote!(-> impl Foo), quote!(-> Box) ); } // With extra bounds #[test] fn impl_trait2() { check_deimplify( quote!(-> impl Foo + Send), quote!(-> Box) ); } } 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 arc() { check_deselfify( quote!(Arc), quote!(Foo), quote!(), quote!(Arc) ); } #[test] fn future() { check_deselfify( quote!(Box>), quote!(Foo), quote!(), quote!(Box>) ); } #[test] fn qself() { check_deselfify( quote!(::Self), quote!(Foo), quote!(), quote!(::Foo) ); } #[test] fn trait_object() { check_deselfify( quote!(Box), quote!(Foo), quote!(), quote!(Box) ); } // A trait object with multiple bounds #[test] fn trait_object2() { check_deselfify( quote!(Box), quote!(Foo), quote!(), quote!(Box) ); } } mod dewhereselfify { use super::*; #[test] fn lifetime() { let mut meth: ImplItemFn = parse2(quote!( fn foo<'a>(&self) where 'a: 'static, Self: Sized {} )).unwrap(); let expected: ImplItemFn = 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: ImplItemFn = parse2(quote!( fn foo(&self) where Self: Sized {} )).unwrap(); let expected: ImplItemFn = parse2(quote!( fn foo(&self) {} )).unwrap(); dewhereselfify(&mut meth.sig.generics); assert_eq!(meth, expected); } #[test] fn with_real_generics() { let mut meth: ImplItemFn = parse2(quote!( fn foo(&self, t: T) where Self: Sized, T: Copy {} )).unwrap(); let expected: ImplItemFn = 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::*; fn check_gen_keyid(orig: TokenStream, expected: TokenStream) { let g: Generics = parse2(orig).unwrap(); let keyid = gen_keyid(&g); assert_eq!(quote!(#keyid).to_string(), quote!(#expected).to_string()); } #[test] fn empty() { check_gen_keyid(quote!(), quote!(<()>)); } #[test] fn onetype() { check_gen_keyid(quote!(), quote!()); } #[test] fn twotypes() { check_gen_keyid(quote!(), quote!(<(T, V)>)); } } mod merge_generics { use super::*; #[test] fn both() { let mut g1: Generics = parse2(quote!( )).unwrap(); let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap(); g1.where_clause = Some(wc1); let mut g2: Generics = parse2(quote!()).unwrap(); let wc2: WhereClause = parse2(quote!(where T: Sync, Q: Debug)).unwrap(); g2.where_clause = Some(wc2); let gm = super::merge_generics(&g1, &g2); let gm_wc = &gm.where_clause; let ge: Generics = parse2(quote!( )).unwrap(); let wce: WhereClause = parse2(quote!( where T: Default + Sync, Q: Debug )).unwrap(); assert_eq!(quote!(#ge #wce).to_string(), quote!(#gm #gm_wc).to_string()); } #[test] fn eq() { let mut g1: Generics = parse2(quote!( )).unwrap(); let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap(); g1.where_clause = Some(wc1.clone()); let gm = super::merge_generics(&g1, &g1); let gm_wc = &gm.where_clause; assert_eq!(quote!(#g1 #wc1).to_string(), quote!(#gm #gm_wc).to_string()); } #[test] fn lhs_only() { let mut g1: Generics = parse2(quote!( )).unwrap(); let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap(); g1.where_clause = Some(wc1.clone()); let g2 = Generics::default(); let gm = super::merge_generics(&g1, &g2); let gm_wc = &gm.where_clause; assert_eq!(quote!(#g1 #wc1).to_string(), quote!(#gm #gm_wc).to_string()); } #[test] fn lhs_wc_only() { let mut g1 = Generics::default(); let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap(); g1.where_clause = Some(wc1.clone()); let g2 = Generics::default(); let gm = super::merge_generics(&g1, &g2); let gm_wc = &gm.where_clause; assert_eq!(quote!(#g1 #wc1).to_string(), quote!(#gm #gm_wc).to_string()); } #[test] fn rhs_only() { let g1 = Generics::default(); let mut g2: Generics = parse2(quote!()).unwrap(); let wc2: WhereClause = parse2(quote!(where T: Sync, Q: Debug)).unwrap(); g2.where_clause = Some(wc2.clone()); let gm = super::merge_generics(&g1, &g2); let gm_wc = &gm.where_clause; assert_eq!(quote!(#g2 #wc2).to_string(), quote!(#gm #gm_wc).to_string()); } } mod supersuperfy { use super::*; fn check_supersuperfy(orig: TokenStream, expected: TokenStream) { let orig_ty: Type = parse2(orig).unwrap(); let expected_ty: Type = parse2(expected).unwrap(); let output = supersuperfy(&orig_ty, 1); assert_eq!(quote!(#output).to_string(), quote!(#expected_ty).to_string()); } #[test] fn array() { check_supersuperfy( quote!([super::X; n]), quote!([super::super::X; n]) ); } #[test] fn barefn() { check_supersuperfy( quote!(fn(super::A) -> super::B), quote!(fn(super::super::A) -> super::super::B) ); } #[test] fn group() { let orig = TypeGroup { group_token: token::Group::default(), elem: Box::new(parse2(quote!(super::T)).unwrap()) }; let expected = TypeGroup { group_token: token::Group::default(), elem: Box::new(parse2(quote!(super::super::T)).unwrap()) }; let output = supersuperfy(&Type::Group(orig), 1); assert_eq!(quote!(#output).to_string(), quote!(#expected).to_string()); } // Just check that it doesn't panic #[test] fn infer() { check_supersuperfy( quote!(_), quote!(_)); } // Just check that it doesn't panic #[test] fn never() { check_supersuperfy( quote!(!), quote!(!)); } #[test] fn paren() { check_supersuperfy( quote!((super::X)), quote!((super::super::X)) ); } #[test] fn path() { check_supersuperfy( quote!(::super::SuperT), quote!(::super::super::SuperT) ); } #[test] fn path_with_qself() { check_supersuperfy( quote!(::Foo), quote!(::Foo), ); } #[test] fn angle_bracketed_generic_arguments() { check_supersuperfy( quote!(mod_::T), quote!(mod_::T) ); } #[test] fn ptr() { check_supersuperfy( quote!(*const super::X), quote!(*const super::super::X) ); } #[test] fn reference() { check_supersuperfy( quote!(&'a mut super::X), quote!(&'a mut super::super::X) ); } #[test] fn slice() { check_supersuperfy( quote!([super::X]), quote!([super::super::X]) ); } #[test] fn trait_object() { check_supersuperfy( quote!(dyn super::X + super::Y), quote!(dyn super::super::X + super::super::Y) ); } #[test] fn tuple() { check_supersuperfy( quote!((super::A, super::B)), quote!((super::super::A, super::super::B)) ); } } mod supersuperfy_generics { use super::*; fn check_supersuperfy_generics( orig: TokenStream, orig_wc: TokenStream, expected: TokenStream, expected_wc: TokenStream) { let mut orig_g: Generics = parse2(orig).unwrap(); orig_g.where_clause = parse2(orig_wc).unwrap(); let mut expected_g: Generics = parse2(expected).unwrap(); expected_g.where_clause = parse2(expected_wc).unwrap(); let mut output: Generics = orig_g; supersuperfy_generics(&mut output, 1); let (o_ig, o_tg, o_wc) = output.split_for_impl(); let (e_ig, e_tg, e_wc) = expected_g.split_for_impl(); assert_eq!(quote!(#o_ig).to_string(), quote!(#e_ig).to_string()); assert_eq!(quote!(#o_tg).to_string(), quote!(#e_tg).to_string()); assert_eq!(quote!(#o_wc).to_string(), quote!(#e_wc).to_string()); } #[test] fn default() { check_supersuperfy_generics( quote!(), quote!(), quote!(), quote!(), ); } #[test] fn empty() { check_supersuperfy_generics(quote!(), quote!(), quote!(), quote!()); } #[test] fn everything() { check_supersuperfy_generics( quote!(), quote!(where super::C: super::D), quote!(), quote!(where super::super::C: super::super::D), ); } #[test] fn bound() { check_supersuperfy_generics( quote!(), quote!(), quote!(), quote!(), ); } #[test] fn closure() { check_supersuperfy_generics( quote!( super::SuperT>), quote!(), quote!( super::super::SuperT>), quote!(), ); } #[test] fn wc_bounded_ty() { check_supersuperfy_generics( quote!(), quote!(where super::T: X), quote!(), quote!(where super::super::T: X), ); } #[test] fn wc_bounds() { check_supersuperfy_generics( quote!(), quote!(where T: super::X), quote!(), quote!(where T: super::super::X), ); } } } mockall_derive-0.13.0/src/mock_function.rs000064400000000000000000003000541046102023000166620ustar 00000000000000// vim: tw=80 use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, format_ident, quote}; use syn::{ *, punctuated::Punctuated, spanned::Spanned }; use crate::{ AttrFormatter, HashSet, compile_error, concretize_args, declosurefy, expectation_visibility, gen_keyid, is_concretize, lifetimes_to_generic_params, lifetimes_to_generics, merge_generics, pat_is_self, split_lifetimes, staticize, supersuperfy, supersuperfy_generics, }; /// Convert a trait object reference into a reference to a Boxed trait /// /// # Returns /// /// Returns `true` if it was necessary to box the type. fn dedynify(ty: &mut Type) -> bool { if let Type::Reference(ref mut tr) = ty { if let Type::TraitObject(ref tto) = tr.elem.as_ref() { if let Some(lt) = &tr.lifetime { if lt.ident == "static" { // For methods that return 'static references, the user can // usually actually supply one, unlike nonstatic references. // dedynify is unneeded and harmful in such cases. // // But we do need to add parens to prevent parsing errors // when methods like returning add a `+ Send` to the output // type. *tr.elem = parse2(quote!((#tto))).unwrap(); return false; } } *tr.elem = parse2(quote!(Box<#tto>)).unwrap(); return true; } } false } /// Convert a special reference type like "&str" into a reference to its owned /// type like "&String". fn destrify(ty: &mut Type) { if let Type::Reference(ref mut tr) = ty { if let Some(lt) = &tr.lifetime { if lt.ident == "static" { // For methods that return 'static references, the user can // usually actually supply one, unlike nonstatic references. // destrify is unneeded and harmful in such cases. return; } } let path_ty: TypePath = parse2(quote!(Path)).unwrap(); let pathbuf_ty: Type = parse2(quote!(::std::path::PathBuf)).unwrap(); let str_ty: TypePath = parse2(quote!(str)).unwrap(); let string_ty: Type = parse2(quote!(::std::string::String)).unwrap(); let cstr_ty: TypePath = parse2(quote!(CStr)).unwrap(); let cstring_ty: Type = parse2(quote!(::std::ffi::CString)).unwrap(); let osstr_ty: TypePath = parse2(quote!(OsStr)).unwrap(); let osstring_ty: Type = parse2(quote!(::std::ffi::OsString)).unwrap(); match tr.elem.as_ref() { Type::Path(ref path) if *path == cstr_ty => *tr.elem = cstring_ty, Type::Path(ref path) if *path == osstr_ty => *tr.elem = osstring_ty, Type::Path(ref path) if *path == path_ty => *tr.elem = pathbuf_ty, Type::Path(ref path) if *path == str_ty => *tr.elem = string_ty, Type::Slice(ts) => { let inner = (*ts.elem).clone(); let mut segments = Punctuated::new(); segments.push(format_ident!("std").into()); segments.push(format_ident!("vec").into()); let mut v: PathSegment = format_ident!("Vec").into(); let mut abga_args = Punctuated::new(); abga_args.push(GenericArgument::Type(inner)); v.arguments = PathArguments::AngleBracketed( AngleBracketedGenericArguments { colon2_token: None, lt_token: Token![<](Span::call_site()), args: abga_args, gt_token: Token![>](Span::call_site()), } ); segments.push(v); *tr.elem = Type::Path(TypePath { qself: None, path: Path { leading_colon: Some(Token![::](Span::call_site())), segments } }); }, _ => (), // Nothing to do }; } } /// Return the owned version of the input. fn ownify(ty: &Type) -> Type { if let Type::Reference(ref tr) = &ty { if tr.lifetime.as_ref().map_or(false, |lt| lt.ident == "static") { // Just a static expectation ty.clone() } else { *tr.elem.clone() } } else { ty.clone() } } /// Add Send + Sync to a where clause fn send_syncify(wc: &mut Option, bounded_ty: Type) { let mut bounds = Punctuated::new(); bounds.push(TypeParamBound::Trait(TraitBound { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: None, path: Path::from(format_ident!("Send")) })); bounds.push(TypeParamBound::Trait(TraitBound { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: None, path: Path::from(format_ident!("Sync")) })); if wc.is_none() { *wc = Some(WhereClause { where_token: ::default(), predicates: Punctuated::new() }); } wc.as_mut().unwrap() .predicates.push( WherePredicate::Type( PredicateType { lifetimes: None, bounded_ty, colon_token: Default::default(), bounds } ) ); } /// Build a MockFunction. #[derive(Clone, Copy, Debug)] pub(crate) struct Builder<'a> { attrs: &'a [Attribute], call_levels: Option, concretize: bool, levels: usize, parent: Option<&'a Ident>, sig: &'a Signature, struct_: Option<&'a Ident>, struct_generics: Option<&'a Generics>, trait_: Option<&'a Ident>, vis: &'a Visibility } impl<'a> Builder<'a> { pub fn attrs(&mut self, attrs: &'a[Attribute]) -> &mut Self { self.attrs = attrs; if attrs.iter() .any(is_concretize) { self.concretize = true; } self } pub fn build(self) -> MockFunction { let mut argnames = Vec::new(); let mut argty = Vec::new(); let mut is_static = true; let mut predexprs = Vec::new(); let mut predty = Vec::new(); let mut refpredty = Vec::new(); let (mut declosured_generics, declosured_inputs, call_exprs, sig) = if self.concretize { let (x, y, z, sig) = concretize_args(&self.sig.generics, self.sig); (x, y, z, sig) } else { let (x, y, z) = declosurefy(&self.sig.generics, &self.sig.inputs); (x, y, z, self.sig.clone()) }; // TODO: make concretize and declosurefy work for the same function for fa in declosured_inputs.iter() { if let FnArg::Typed(pt) = fa { let argname = (*pt.pat).clone(); assert!(!pat_is_self(&argname)); let aty = supersuperfy(&pt.ty, self.levels); if let Type::Reference(ref tr) = aty { predexprs.push(quote!(#argname)); predty.push((*tr.elem).clone()); let tr2 = Type::Reference(TypeReference { and_token: tr.and_token, lifetime: None, mutability: None, elem: tr.elem.clone() }); refpredty.push(tr2); } else { predexprs.push(quote!(&#argname)); predty.push(aty.clone()); let tr = TypeReference { and_token: Token![&](Span::call_site()), lifetime: None, mutability: None, elem: Box::new(aty.clone()) }; refpredty.push(Type::Reference(tr)); }; argnames.push(argname); argty.push(aty.clone()); } else { is_static = false; } } let (output, boxed) = match self.sig.output { ReturnType::Default => ( Type::Tuple(TypeTuple { paren_token: token::Paren::default(), elems: Punctuated::new(), }), false, ), ReturnType::Type(_, ref ty) => { let mut output_ty = supersuperfy(ty, self.levels); destrify(&mut output_ty); let boxed = dedynify(&mut output_ty); (output_ty, boxed) } }; supersuperfy_generics(&mut declosured_generics, self.levels); let owned_output = ownify(&output); let mut return_ref = false; let mut return_refmut = false; if let Type::Reference(ref tr) = &output { if tr.lifetime.as_ref().map_or(true, |lt| lt.ident != "static") { if tr.mutability.is_none() { return_ref = true; } else { return_refmut = true; } } }; if is_static && (return_ref || return_refmut) { compile_error(self.sig.span(), "Mockall cannot mock static methods that return non-'static references. It's unclear what the return value's lifetime should be."); } let struct_generics = self.struct_generics.cloned() .unwrap_or_default(); let (type_generics, salifetimes, srlifetimes) = split_lifetimes( struct_generics.clone(), &declosured_inputs, &ReturnType::Type(]>::default(), Box::new(owned_output.clone())) ); let srltg = lifetimes_to_generics(&srlifetimes); let (call_generics, malifetimes, mrlifetimes) = split_lifetimes( declosured_generics, &declosured_inputs, &ReturnType::Type(]>::default(), Box::new(owned_output.clone())) ); let mrltg = lifetimes_to_generics(&mrlifetimes); let cgenerics = merge_generics(&type_generics, &call_generics); let egenerics = merge_generics( &merge_generics(&cgenerics, &srltg), &mrltg); let alifetimes = salifetimes.into_iter() .collect::>() .union(&malifetimes.into_iter().collect::>()) .cloned() .collect(); let fn_params = egenerics.type_params() .map(|tp| tp.ident.clone()) .collect(); let call_levels = self.call_levels.unwrap_or(self.levels); MockFunction { alifetimes, argnames, argty, attrs: self.attrs.to_vec(), call_exprs, call_generics, call_vis: expectation_visibility(self.vis, call_levels), concretize: self.concretize, egenerics, cgenerics, fn_params, is_static, mod_ident: self.parent.unwrap_or(&Ident::new("FIXME", Span::call_site())).clone(), output, owned_output, boxed, predexprs, predty, refpredty, return_ref, return_refmut, sig, struct_: self.struct_.cloned(), struct_generics, trait_: self.trait_.cloned(), type_generics, privmod_vis: expectation_visibility(self.vis, self.levels) } } /// How many levels of modules beneath the original function this one is /// nested. 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: usize) -> &mut Self { self.levels = levels; self } /// # Arguments /// /// * sig: The signature of the mockable function /// * v: The visibility of the mockable function pub fn new(sig: &'a Signature, vis: &'a Visibility) -> Self { Builder { attrs: &[], concretize: false, levels: 0, call_levels: None, parent: None, sig, struct_: None, struct_generics: None, trait_: None, vis } } /// Supply the name of the parent module pub fn parent(&mut self, ident: &'a Ident) -> &mut Self { self.parent = Some(ident); self } /// Supply the name of the parent struct, if any pub fn struct_(&mut self, ident: &'a Ident) -> &mut Self { self.struct_= Some(ident); self } /// Supply the Generics of the parent struct, if any pub fn struct_generics(&mut self, generics: &'a Generics) -> &mut Self { self.struct_generics = Some(generics); self } /// Supply the name of the method's trait, if any pub fn trait_(&mut self, ident: &'a Ident) -> &mut Self { self.trait_ = Some(ident); self } } #[derive(Clone)] pub(crate) struct MockFunction { /// Lifetimes of the mocked method that relate to the arguments but not the /// return value alifetimes: Punctuated, /// Names of the method arguments argnames: Vec, /// Types of the method arguments argty: Vec, /// any attributes on the original function, like #[inline] pub attrs: Vec, /// Expressions that should be used for Expectation::call's arguments call_exprs: Vec, /// Generics used for the expectation call call_generics: Generics, /// Visibility of the mock function itself call_vis: Visibility, /// Are we turning generic arguments into concrete trait objects? concretize: bool, /// Generics of the Expectation object egenerics: Generics, /// Generics of the Common object cgenerics: Generics, /// The mock function's generic types as a list of types fn_params: Vec, /// Is this for a static method or free function? is_static: bool, /// name of the function's parent module mod_ident: Ident, /// Output type of the Method, supersuperfied. output: Type, /// Owned version of the output type of the Method, supersuperfied. /// /// If the real output type is a non-'static reference, then it will differ /// from this field. owned_output: Type, /// True if the `owned_type` is boxed by `Box<>`. boxed: bool, /// Expressions that create the predicate arguments from the call arguments predexprs: Vec, /// Types used for Predicates. Will be almost the same as args, but every /// type will be a non-reference type. predty: Vec, /// Does the function return a non-'static reference? return_ref: bool, /// Does the function return a mutable reference? return_refmut: bool, /// References to every type in `predty`. refpredty: Vec, /// The signature of the mockable function sig: Signature, /// Name of the parent structure, if any struct_: Option, /// Generics of the parent structure struct_generics: Generics, /// Name of this method's trait, if the method comes from a trait trait_: Option, /// Type generics of the mock structure type_generics: Generics, /// Visibility of the expectation and its methods privmod_vis: Visibility } impl MockFunction { /// Return the mock function itself /// /// # Arguments /// /// * `modname`: Name of the parent struct's private module // Supplying modname is an unfortunately hack. Ideally MockFunction // wouldn't need to know that. pub fn call(&self, modname: Option<&Ident>) -> impl ToTokens { let attrs = AttrFormatter::new(&self.attrs) .must_use(true) .format(); let call_exprs = &self.call_exprs; let (_, tg, _) = if self.is_method_generic() || self.is_static() { &self.egenerics } else { &self.call_generics }.split_for_impl(); let tbf = tg.as_turbofish(); let name = self.name(); let desc = self.desc(); let no_match_msg = quote!(std::format!( "{}: No matching expectation found", #desc)); let sig = &self.sig; let (vis, dead_code) = if self.trait_.is_some() { (&Visibility::Inherited, quote!()) } else { let dead_code = if let Visibility::Inherited = self.call_vis { // This private method may be a helper only used by the struct's // other methods, which we are mocking. If so, the mock method // will be dead code. But we can't simply eliminate it, because // it might also be used by other code in the same module. quote!(#[allow(dead_code)]) } else { quote!() }; (&self.call_vis, dead_code) }; // Add #[no_mangle] attribute to preserve the function name // as-is, without mangling, for compatibility with C functions. let no_mangle = if let Some(ref abi) = self.sig.abi { if let Some(ref name) = abi.name { if name.value().ne("Rust") { quote!(#[no_mangle]) } else { quote!() } } else { // This is the same as extern "C" quote!(#[no_mangle]) } } else { quote!() }; let substruct_obj: TokenStream = if let Some(trait_) = &self.trait_ { let ident = format_ident!("{}_expectations", trait_); quote!(#ident.) } else { quote!() }; let call = if self.return_refmut { Ident::new("call_mut", Span::call_site()) } else { Ident::new("call", Span::call_site()) }; let mut deref = quote!(); if self.boxed { if self.return_ref { deref = quote!(&**); } else if self.return_refmut { deref = quote!(&mut **); } } if self.is_static { let outer_mod_path = self.outer_mod_path(modname); quote!( // Don't add a doc string. The original is included in #attrs #(#attrs)* #dead_code #no_mangle #vis #sig { use ::mockall::{ViaDebug, ViaNothing}; let no_match_msg = #no_match_msg; #deref { let __mockall_guard = #outer_mod_path::get_expectations() .lock().unwrap(); /* * TODO: catch panics, then gracefully release the mutex * so it won't be poisoned. This requires bounding any * generic parameters with UnwindSafe */ /* std::panic::catch_unwind(|| */ __mockall_guard.#call #tbf(#(#call_exprs,)*) /*)*/ }.expect(&no_match_msg) } ) } else { quote!( // Don't add a doc string. The original is included in #attrs #(#attrs)* #dead_code #no_mangle #vis #sig { use ::mockall::{ViaDebug, ViaNothing}; let no_match_msg = #no_match_msg; #deref self.#substruct_obj #name.#call #tbf(#(#call_exprs,)*) .expect(&no_match_msg) } ) } } /// Return this method's contribution to its parent's checkpoint method pub fn checkpoint(&self) -> impl ToTokens { let attrs = AttrFormatter::new(&self.attrs) .doc(false) .format(); let inner_mod_ident = self.inner_mod_ident(); if self.is_static { quote!( #(#attrs)* { let __mockall_timeses = #inner_mod_ident::get_expectations().lock() .unwrap() .checkpoint() .collect::>(); } ) } else { let name = &self.name(); quote!(#(#attrs)* { self.#name.checkpoint(); }) } } /// Return a function that creates a Context object for this function /// /// # Arguments /// /// * `modname`: Name of the parent struct's private module // Supplying modname is an unfortunately hack. Ideally MockFunction // wouldn't need to know that. pub fn context_fn(&self, modname: Option<&Ident>) -> impl ToTokens { let attrs = AttrFormatter::new(&self.attrs) .doc(false) .format(); let context_docstr = format!("Create a [`Context`]({}{}/struct.Context.html) for mocking the `{}` method", modname.map(|m| format!("{m}/")).unwrap_or_default(), self.inner_mod_ident(), self.name()); let context_ident = format_ident!("{}_context", self.name()); let (_, tg, _) = self.type_generics.split_for_impl(); let outer_mod_path = self.outer_mod_path(modname); let v = &self.call_vis; quote!( #(#attrs)* #[doc = #context_docstr] #v fn #context_ident() -> #outer_mod_path::Context #tg { #outer_mod_path::Context::default() } ) } /// Generate a code fragment that will print a description of the invocation fn desc(&self) -> impl ToTokens { let argnames = &self.argnames; let name = if let Some(s) = &self.struct_ { format!("{}::{}", s, self.sig.ident) } else { format!("{}::{}", self.mod_ident, self.sig.ident) }; let fields = vec!["{:?}"; argnames.len()].join(", "); let fstr = format!("{name}({fields})"); quote!(std::format!(#fstr, #((&&::mockall::ArgPrinter(&#argnames)).debug_string()),*)) } /// Generate code for the expect_ method /// /// # Arguments /// /// * `modname`: Name of the parent struct's private module /// * `self_args`: If supplied, these are the /// AngleBracketedGenericArguments of the self type of the /// trait impl. e.g. The `T` in `impl Foo for Bar`. // Supplying modname is an unfortunately hack. Ideally MockFunction // wouldn't need to know that. pub fn expect(&self, modname: &Ident, self_args: Option<&PathArguments>) -> impl ToTokens { let attrs = AttrFormatter::new(&self.attrs) .doc(false) .format(); let name = self.name(); let expect_ident = format_ident!("expect_{}", name); let expectation_obj = self.expectation_obj(self_args); let funcname = &self.sig.ident; let (_, tg, _) = if self.is_method_generic() { &self.egenerics } else { &self.call_generics }.split_for_impl(); let (ig, _, wc) = self.call_generics.split_for_impl(); let mut wc = wc.cloned(); if self.is_method_generic() && (self.return_ref || self.return_refmut) { // Add Senc + Sync, required for downcast, since Expectation // stores an Option<#owned_output> send_syncify(&mut wc, self.owned_output.clone()); } let tbf = tg.as_turbofish(); let vis = &self.call_vis; #[cfg(not(feature = "nightly_derive"))] let must_use = quote!(#[must_use = "Must set return value when not using the \"nightly\" feature" ]); #[cfg(feature = "nightly_derive")] let must_use = quote!(); let substruct_obj = if let Some(trait_) = &self.trait_ { let ident = format_ident!("{trait_}_expectations"); quote!(#ident.) } else { quote!() }; let docstr = format!("Create an [`Expectation`]({}/{}/struct.Expectation.html) for mocking the `{}` method", modname, self.inner_mod_ident(), funcname); quote!( #must_use #[doc = #docstr] #(#attrs)* #vis fn #expect_ident #ig(&mut self) -> &mut #modname::#expectation_obj #wc { self.#substruct_obj #name.expect #tbf() } ) } /// Return the name of this function's expecation object fn expectation_obj(&self, self_args: Option<&PathArguments>) -> impl ToTokens { let inner_mod_ident = self.inner_mod_ident(); if let Some(PathArguments::AngleBracketed(abga)) = self_args { // staticize any lifetimes that might be present in the Expectation // object but not in the self args. These come from the method's // return type. let mut abga2 = abga.clone(); for _ in self.egenerics.lifetimes() { let lt = Lifetime::new("'static", Span::call_site()); let la = GenericArgument::Lifetime(lt); abga2.args.insert(0, la); } assert!(!self.is_method_generic(), "specific impls with generic methods are TODO"); quote!(#inner_mod_ident::Expectation #abga2) } else { // staticize any lifetimes. This is necessary for methods that // return non-static types, because the Expectation itself must be // 'static. let segenerics = staticize(&self.egenerics); let (_, tg, _) = segenerics.split_for_impl(); quote!(#inner_mod_ident::Expectation #tg) } } /// Return the name of this function's expecations object pub fn expectations_obj(&self) -> impl ToTokens { let inner_mod_ident = self.inner_mod_ident(); if self.is_method_generic() { quote!(#inner_mod_ident::GenericExpectations) } else { quote!(#inner_mod_ident::Expectations) } } pub fn field_definition(&self, modname: Option<&Ident>) -> TokenStream { let name = self.name(); let attrs = AttrFormatter::new(&self.attrs) .doc(false) .format(); let expectations_obj = &self.expectations_obj(); if self.is_method_generic() { quote!(#(#attrs)* #name: #modname::#expectations_obj) } else { // staticize any lifetimes. This is necessary for methods that // return non-static types, because the Expectation itself must be // 'static. let segenerics = staticize(&self.egenerics); let (_, tg, _) = segenerics.split_for_impl(); quote!(#(#attrs)* #name: #modname::#expectations_obj #tg) } } /// Human-readable name of the mock function fn funcname(&self) -> String { if let Some(si) = &self.struct_ { format!("{}::{}", si, self.name()) } else { format!("{}", self.name()) } } fn hrtb(&self) -> Option { if self.alifetimes.is_empty() { None } else { let lifetimes = lifetimes_to_generic_params(&self.alifetimes); Some(BoundLifetimes { lifetimes, lt_token: ::default(), gt_token: ]>::default(), .. Default::default() }) } } fn is_expectation_generic(&self) -> bool { self.egenerics.params.iter().any(|p| { matches!(p, GenericParam::Type(_)) }) || self.egenerics.where_clause.is_some() } /// Is the mock method generic (as opposed to a non-generic method of a /// generic mock struct)? pub fn is_method_generic(&self) -> bool { self.call_generics.params.iter().any(|p| { matches!(p, GenericParam::Type(_)) }) || self.call_generics.where_clause.is_some() } fn outer_mod_path(&self, modname: Option<&Ident>) -> Path { let mut path = if let Some(m) = modname { Path::from(PathSegment::from(m.clone())) } else { Path { leading_colon: None, segments: Punctuated::new() } }; path.segments.push(PathSegment::from(self.inner_mod_ident())); path } fn inner_mod_ident(&self) -> Ident { format_ident!("__{}", &self.name()) } pub fn is_static(&self) -> bool { self.is_static } pub fn name(&self) -> &Ident { &self.sig.ident } /// Generate code for this function's private module pub fn priv_module(&self) -> impl ToTokens { let attrs = AttrFormatter::new(&self.attrs) .doc(false) .format(); let common = &Common{f: self}; let context = &Context{f: self}; let expectation: Box = if self.return_ref { Box::new(RefExpectation{f: self}) } else if self.return_refmut { Box::new(RefMutExpectation{f: self}) } else { Box::new(StaticExpectation{f: self}) }; let expectations: Box = if self.return_ref { Box::new(RefExpectations{f: self}) } else if self.return_refmut { Box::new(RefMutExpectations{f: self}) } else { Box::new(StaticExpectations{f: self}) }; let generic_expectations = GenericExpectations{f: self}; let guard: Box = if self.is_expectation_generic() { Box::new(GenericExpectationGuard{f: self}) } else { Box::new(ConcreteExpectationGuard{f: self}) }; let matcher = &Matcher{f: self}; let std_mutexguard = if self.is_static { quote!(use ::std::sync::MutexGuard;) } else { quote!() }; let inner_mod_ident = self.inner_mod_ident(); let rfunc: Box = if self.return_ref { Box::new(RefRfunc{f: self}) } else if self.return_refmut { Box::new(RefMutRfunc{f: self}) } else { Box::new(StaticRfunc{f: self}) }; quote!( #(#attrs)* #[allow(missing_docs)] #[allow(clippy::too_many_arguments, clippy::indexing_slicing)] pub mod #inner_mod_ident { use super::*; use ::mockall::CaseTreeExt; #std_mutexguard use ::std::{ boxed::Box, mem, ops::{DerefMut, Range}, sync::Mutex, vec::Vec, }; #rfunc #matcher #common #expectation #expectations #generic_expectations #guard #context } ) } } /// Holds parts of the expectation that are common for all output types struct Common<'a> { f: &'a MockFunction } impl<'a> ToTokens for Common<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let argnames = &self.f.argnames; let predty = &self.f.predty; let hrtb = self.f.hrtb(); let funcname = self.f.funcname(); let (ig, tg, wc) = self.f.cgenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let refpredty = &self.f.refpredty; let with_generics_idents = (0..self.f.predty.len()) .map(|i| format_ident!("MockallMatcher{i}")) .collect::>(); let with_generics = with_generics_idents.iter() .zip(self.f.predty.iter()) .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ) ).collect::(); let with_args = self.f.argnames.iter() .zip(with_generics_idents.iter()) .map(|(argname, id)| quote!(#argname: #id, )) .collect::(); let boxed_withargs = argnames.iter() .map(|aa| quote!(Box::new(#aa), )) .collect::(); let with_method = if self.f.concretize { quote!( // No `with` method when concretizing generics ) } else { quote!( fn with<#with_generics>(&mut self, #with_args) { let mut __mockall_guard = self.matcher.lock().unwrap(); *__mockall_guard.deref_mut() = Matcher::Pred(Box::new((#boxed_withargs))); } ) }; quote!( /// Holds the stuff that is independent of the output type struct Common #ig #wc { matcher: Mutex, seq_handle: Option<::mockall::SeqHandle>, times: ::mockall::Times } impl #ig std::default::Default for Common #tg #wc { fn default() -> Self { Common { matcher: Mutex::new(Matcher::default()), seq_handle: None, times: ::mockall::Times::default() } } } impl #ig Common #tg #wc { fn call(&self, desc: &str) { self.times.call() .unwrap_or_else(|m| { let desc = std::format!( "{}", self.matcher.lock().unwrap()); panic!("{}: Expectation({}) {}", #funcname, desc, m); }); self.verify_sequence(desc); if ::mockall::ExpectedCalls::TooFew != self.times.is_satisfied() { self.satisfy_sequence() } } fn in_sequence(&mut self, __mockall_seq: &mut ::mockall::Sequence) -> &mut Self { assert!(self.times.is_exact(), "Only Expectations with an exact call count have sequences"); self.seq_handle = Some(__mockall_seq.next_handle()); self } fn is_done(&self) -> bool { self.times.is_done() } #[allow(clippy::ptr_arg)] fn matches #lg (&self, #( #argnames: &#predty, )*) -> bool { self.matcher.lock().unwrap().matches(#(#argnames, )*) } /// Forbid this expectation from ever being called. fn never(&mut self) { self.times.never(); } fn satisfy_sequence(&self) { if let Some(__mockall_handle) = &self.seq_handle { __mockall_handle.satisfy() } } /// Expect this expectation to be called any number of times /// contained with the given range. fn times(&mut self, __mockall_r: MockallR) where MockallR: Into<::mockall::TimesRange> { self.times.times(__mockall_r) } #with_method fn withf(&mut self, __mockall_f: MockallF) where MockallF: #hrtb Fn(#( #refpredty, )*) -> bool + Send + 'static { let mut __mockall_guard = self.matcher.lock().unwrap(); *__mockall_guard.deref_mut() = Matcher::Func(Box::new(__mockall_f)); } fn withf_st(&mut self, __mockall_f: MockallF) where MockallF: #hrtb Fn(#( #refpredty, )*) -> bool + 'static { let mut __mockall_guard = self.matcher.lock().unwrap(); *__mockall_guard.deref_mut() = Matcher::FuncSt( ::mockall::Fragile::new(Box::new(__mockall_f)) ); } fn verify_sequence(&self, desc: &str) { if let Some(__mockall_handle) = &self.seq_handle { __mockall_handle.verify(desc) } } } impl #ig Drop for Common #tg #wc { fn drop(&mut self) { if !::std::thread::panicking() { let desc = std::format!( "{}", self.matcher.lock().unwrap()); match self.times.is_satisfied() { ::mockall::ExpectedCalls::TooFew => { panic!("{}: Expectation({}) called {} time(s) which is fewer than expected {}", #funcname, desc, self.times.count(), self.times.minimum()); }, ::mockall::ExpectedCalls::TooMany => { panic!("{}: Expectation({}) called {} time(s) which is more than expected {}", #funcname, desc, self.times.count(), self.times.maximum()); }, _ => () } } } } ).to_tokens(tokens); } } /// Generates methods that are common for all Expectation types struct CommonExpectationMethods<'a> { f: &'a MockFunction } impl<'a> ToTokens for CommonExpectationMethods<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let argnames = &self.f.argnames; let hrtb = self.f.hrtb(); let lg = lifetimes_to_generics(&self.f.alifetimes); let predty = &self.f.predty; let with_generics_idents = (0..self.f.predty.len()) .map(|i| format_ident!("MockallMatcher{i}")) .collect::>(); let with_generics = with_generics_idents.iter() .zip(self.f.predty.iter()) .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ) ).collect::(); let with_args = self.f.argnames.iter() .zip(with_generics_idents.iter()) .map(|(argname, id)| quote!(#argname: #id, )) .collect::(); let v = &self.f.privmod_vis; let with_method = if self.f.concretize { quote!( // No `with` method when concretizing generics ) } else { quote!( /// Set matching criteria for this Expectation. /// /// The matching predicate can be anything implemening the /// [`Predicate`](../../../mockall/trait.Predicate.html) trait. Only /// one matcher can be set per `Expectation` at a time. #v fn with<#with_generics>(&mut self, #with_args) -> &mut Self { self.common.with(#(#argnames, )*); self } ) }; quote!( /// Add this expectation to a /// [`Sequence`](../../../mockall/struct.Sequence.html). #v fn in_sequence(&mut self, __mockall_seq: &mut ::mockall::Sequence) -> &mut Self { self.common.in_sequence(__mockall_seq); self } fn is_done(&self) -> bool { self.common.is_done() } /// Validate this expectation's matcher. #[allow(clippy::ptr_arg)] fn matches #lg (&self, #(#argnames: &#predty, )*) -> bool { self.common.matches(#(#argnames, )*) } /// Forbid this expectation from ever being called. #v fn never(&mut self) -> &mut Self { self.common.never(); self } /// Create a new, default, [`Expectation`](struct.Expectation.html) #v fn new() -> Self { Self::default() } /// Expect this expectation to be called exactly once. Shortcut for /// [`times(1)`](#method.times). #v fn once(&mut self) -> &mut Self { self.times(1) } /// Restrict the number of times that that this method may be called. /// /// The argument may be: /// * A fixed number: `.times(4)` /// * Various types of range: /// - `.times(5..10)` /// - `.times(..10)` /// - `.times(5..)` /// - `.times(5..=10)` /// - `.times(..=10)` /// * The wildcard: `.times(..)` #v fn times(&mut self, __mockall_r: MockallR) -> &mut Self where MockallR: Into<::mockall::TimesRange> { self.common.times(__mockall_r); self } #with_method /// Set a matching function for this Expectation. /// /// This is equivalent to calling [`with`](#method.with) with a /// function argument, like `with(predicate::function(f))`. #v fn withf(&mut self, __mockall_f: MockallF) -> &mut Self where MockallF: #hrtb Fn(#(&#predty, )*) -> bool + Send + 'static { self.common.withf(__mockall_f); self } /// Single-threaded version of [`withf`](#method.withf). /// Can be used when the argument type isn't `Send`. #v fn withf_st(&mut self, __mockall_f: MockallF) -> &mut Self where MockallF: #hrtb Fn(#(&#predty, )*) -> bool + 'static { self.common.withf_st(__mockall_f); self } ).to_tokens(tokens); } } /// Holds the moethods of the Expectations object that are common for all /// Expectation types struct CommonExpectationsMethods<'a> { f: &'a MockFunction } impl<'a> ToTokens for CommonExpectationsMethods<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let v = &self.f.privmod_vis; quote!( /// A collection of [`Expectation`](struct.Expectations.html) /// objects. Users will rarely if ever use this struct directly. #[doc(hidden)] #v struct Expectations #ig ( Vec) #wc; impl #ig Expectations #tg #wc { /// Verify that all current expectations are satisfied and clear /// them. #v fn checkpoint(&mut self) -> std::vec::Drain { self.0.drain(..) } /// Create a new expectation for this method. #v fn expect(&mut self) -> &mut Expectation #tg { self.0.push(Expectation::default()); let __mockall_l = self.0.len(); &mut self.0[__mockall_l - 1] } #v const fn new() -> Self { Self(Vec::new()) } } impl #ig Default for Expectations #tg #wc { fn default() -> Self { Expectations::new() } } ).to_tokens(tokens); } } /// The ExpectationGuard structure for static methods with no generic types struct ExpectationGuardCommonMethods<'a> { f: &'a MockFunction } impl<'a> ToTokens for ExpectationGuardCommonMethods<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { if !self.f.is_static { return; } let argnames = &self.f.argnames; let argty = &self.f.argty; let (_, tg, _) = self.f.egenerics.split_for_impl(); let keyid = gen_keyid(&self.f.egenerics); let expectations = if self.f.is_expectation_generic() { quote!(self.guard .store .get_mut(&::mockall::Key::new::#keyid()) .unwrap() .downcast_mut::() .unwrap()) } else { quote!(self.guard) }; let hrtb = self.f.hrtb(); let output = &self.f.output; let predty = &self.f.predty; let with_generics_idents = (0..self.f.predty.len()) .map(|i| format_ident!("MockallMatcher{i}")) .collect::>(); let with_generics = with_generics_idents.iter() .zip(self.f.predty.iter()) .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ) ).collect::(); let with_args = self.f.argnames.iter() .zip(with_generics_idents.iter()) .map(|(argname, id)| quote!(#argname: #id, )) .collect::(); let v = &self.f.privmod_vis; let with_method = if self.f.concretize { quote!() } else { quote!( /// Just like /// [`Expectation::with`](struct.Expectation.html#method.with) #v fn with<#with_generics> (&mut self, #with_args) -> &mut Expectation #tg { #expectations.0[self.i].with(#(#argnames, )*) } ) }; quote!( /// Just like /// [`Expectation::in_sequence`](struct.Expectation.html#method.in_sequence) #v fn in_sequence(&mut self, __mockall_seq: &mut ::mockall::Sequence) -> &mut Expectation #tg { #expectations.0[self.i].in_sequence(__mockall_seq) } /// Just like /// [`Expectation::never`](struct.Expectation.html#method.never) #v fn never(&mut self) -> &mut Expectation #tg { #expectations.0[self.i].never() } /// Just like /// [`Expectation::once`](struct.Expectation.html#method.once) #v fn once(&mut self) -> &mut Expectation #tg { #expectations.0[self.i].once() } /// Just like /// [`Expectation::return_const`](struct.Expectation.html#method.return_const) #v fn return_const (&mut self, __mockall_c: MockallOutput) -> &mut Expectation #tg where MockallOutput: Clone + Into<#output> + Send + 'static { #expectations.0[self.i].return_const(__mockall_c) } /// Just like /// [`Expectation::return_const_st`](struct.Expectation.html#method.return_const_st) #v fn return_const_st (&mut self, __mockall_c: MockallOutput) -> &mut Expectation #tg where MockallOutput: Clone + Into<#output> + 'static { #expectations.0[self.i].return_const_st(__mockall_c) } /// Just like /// [`Expectation::returning`](struct.Expectation.html#method.returning) #v fn returning(&mut self, __mockall_f: MockallF) -> &mut Expectation #tg where MockallF: #hrtb FnMut(#(#argty, )*) -> #output + Send + 'static { #expectations.0[self.i].returning(__mockall_f) } /// Just like /// [`Expectation::return_once`](struct.Expectation.html#method.return_once) #v fn return_once(&mut self, __mockall_f: MockallF) -> &mut Expectation #tg where MockallF: #hrtb FnOnce(#(#argty, )*) -> #output + Send + 'static { #expectations.0[self.i].return_once(__mockall_f) } /// Just like /// [`Expectation::return_once_st`](struct.Expectation.html#method.return_once_st) #v fn return_once_st(&mut self, __mockall_f: MockallF) -> &mut Expectation #tg where MockallF: #hrtb FnOnce(#(#argty, )*) -> #output + 'static { #expectations.0[self.i].return_once_st(__mockall_f) } /// Just like /// [`Expectation::returning_st`](struct.Expectation.html#method.returning_st) #v fn returning_st(&mut self, __mockall_f: MockallF) -> &mut Expectation #tg where MockallF: #hrtb FnMut(#(#argty, )*) -> #output + 'static { #expectations.0[self.i].returning_st(__mockall_f) } /// Just like /// [`Expectation::times`](struct.Expectation.html#method.times) #v fn times(&mut self, __mockall_r: MockallR) -> &mut Expectation #tg where MockallR: Into<::mockall::TimesRange> { #expectations.0[self.i].times(__mockall_r) } #with_method /// Just like /// [`Expectation::withf`](struct.Expectation.html#method.withf) #v fn withf(&mut self, __mockall_f: MockallF) -> &mut Expectation #tg where MockallF: #hrtb Fn(#(&#predty, )*) -> bool + Send + 'static { #expectations.0[self.i].withf(__mockall_f) } /// Just like /// [`Expectation::withf_st`](struct.Expectation.html#method.withf_st) #v fn withf_st(&mut self, __mockall_f: MockallF) -> &mut Expectation #tg where MockallF: #hrtb Fn(#(&#predty, )*) -> bool + 'static { #expectations.0[self.i].withf_st(__mockall_f) } ).to_tokens(tokens); } } /// The ExpectationGuard structure for static methods with no generic types struct ConcreteExpectationGuard<'a> { f: &'a MockFunction } impl<'a> ToTokens for ConcreteExpectationGuard<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { if !self.f.is_static { return; } let common_methods = ExpectationGuardCommonMethods{f: self.f}; let (_, tg, _) = self.f.egenerics.split_for_impl(); let ltdef = LifetimeParam::new( Lifetime::new("'__mockall_lt", Span::call_site()) ); let mut e_generics = self.f.egenerics.clone(); e_generics.lt_token.get_or_insert(::default()); e_generics.params.push(GenericParam::Lifetime(ltdef)); e_generics.gt_token.get_or_insert(]>::default()); let (e_ig, e_tg, e_wc) = e_generics.split_for_impl(); let (ei_ig, _, _) = e_generics.split_for_impl(); let v = &self.f.privmod_vis; quote!( #[doc(hidden)] #v fn get_expectations() -> &'static ::std::sync::Mutex { static EXPECTATIONS: ::std::sync::Mutex = ::std::sync::Mutex::new(Expectations::new()); &EXPECTATIONS } /// Like an [`&Expectation`](struct.Expectation.html) but /// protected by a Mutex guard. Useful for mocking static /// methods. Forwards accesses to an `Expectation` object. // We must return the MutexGuard to the caller so he can // configure the expectation. But we can't bundle both the // guard and the &Expectation into the same structure; the // borrow checker won't let us. Instead we'll record the // expectation's position within the Expectations vector so we // can proxy its methods. // // ExpectationGuard is only defined for expectations that return // 'static return types. #v struct ExpectationGuard #e_ig #e_wc { guard: MutexGuard<'__mockall_lt, Expectations #tg>, i: usize } #[allow(clippy::unused_unit)] impl #ei_ig ExpectationGuard #e_tg #e_wc { // Should only be called from the mockall_derive generated // code #[doc(hidden)] #v fn new(mut __mockall_guard: MutexGuard<'__mockall_lt, Expectations #tg>) -> Self { __mockall_guard.expect(); // Drop the &Expectation let __mockall_i = __mockall_guard.0.len() - 1; ExpectationGuard{guard: __mockall_guard, i: __mockall_i} } #common_methods } ).to_tokens(tokens); } } /// The ExpectationGuard structure for static methods with generic types struct GenericExpectationGuard<'a> { f: &'a MockFunction } impl<'a> ToTokens for GenericExpectationGuard<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { if !self.f.is_static { return; } let common_methods = ExpectationGuardCommonMethods{f: self.f}; let (_, tg, _) = self.f.egenerics.split_for_impl(); let keyid = gen_keyid(&self.f.egenerics); let ltdef = LifetimeParam::new( Lifetime::new("'__mockall_lt", Span::call_site()) ); let mut egenerics = self.f.egenerics.clone(); egenerics.lt_token.get_or_insert(::default()); egenerics.params.push(GenericParam::Lifetime(ltdef)); egenerics.gt_token.get_or_insert(]>::default()); let (e_ig, e_tg, e_wc) = egenerics.split_for_impl(); let fn_params = &self.f.fn_params; let tbf = tg.as_turbofish(); let v = &self.f.privmod_vis; quote!( #[doc(hidden)] #v fn get_expectations() -> &'static ::std::sync::Mutex { static CELL: ::std::sync::OnceLock<::std::sync::Mutex> = ::std::sync::OnceLock::new(); CELL.get_or_init(|| ::std::sync::Mutex::new(GenericExpectations::new())) } /// Like an [`&Expectation`](struct.Expectation.html) but /// protected by a Mutex guard. Useful for mocking static /// methods. Forwards accesses to an `Expectation` object. #v struct ExpectationGuard #e_ig #e_wc{ guard: MutexGuard<'__mockall_lt, GenericExpectations>, i: usize, _phantom: ::std::marker::PhantomData<(#(#fn_params,)*)>, } #[allow(clippy::unused_unit)] impl #e_ig ExpectationGuard #e_tg #e_wc { // Should only be called from the mockall_derive generated // code #[doc(hidden)] #v fn new(mut __mockall_guard: MutexGuard<'__mockall_lt, GenericExpectations>) -> Self { let __mockall_ee: &mut Expectations #tg = __mockall_guard.store.entry( ::mockall::Key::new::#keyid() ).or_insert_with(|| Box::new(Expectations #tbf ::new())) .downcast_mut() .unwrap(); __mockall_ee.expect(); // Drop the &Expectation let __mockall_i = __mockall_ee.0.len() - 1; ExpectationGuard{guard: __mockall_guard, i: __mockall_i, _phantom: ::std::marker::PhantomData} } #common_methods } ).to_tokens(tokens); } } /// Generates Context, which manages the context for expectations of static /// methods. struct Context<'a> { f: &'a MockFunction } impl<'a> ToTokens for Context<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { if !self.f.is_static { return; } let ltdef = LifetimeParam::new( Lifetime::new("'__mockall_lt", Span::call_site()) ); let mut egenerics = self.f.egenerics.clone(); egenerics.lt_token.get_or_insert(::default()); egenerics.params.push(GenericParam::Lifetime(ltdef)); egenerics.gt_token.get_or_insert(]>::default()); let (_, e_tg, _) = egenerics.split_for_impl(); let (ty_ig, ty_tg, ty_wc) = self.f.type_generics.split_for_impl(); let mut meth_generics = self.f.call_generics.clone(); let ltdef = LifetimeParam::new( Lifetime::new("'__mockall_lt", Span::call_site()) ); meth_generics.params.push(GenericParam::Lifetime(ltdef)); let (meth_ig, _meth_tg, meth_wc) = meth_generics.split_for_impl(); let ctx_fn_params = self.f.struct_generics.type_params() .map(|tp| tp.ident.clone()) .collect::>(); let v = &self.f.privmod_vis; #[cfg(not(feature = "nightly_derive"))] let must_use = quote!(#[must_use = "Must set return value when not using the \"nightly\" feature" ]); #[cfg(feature = "nightly_derive")] let must_use = quote!(); #[cfg(not(feature = "nightly_derive"))] let clear_poison = quote!(); #[cfg(feature = "nightly_derive")] let clear_poison = quote!( #[allow(clippy::incompatible_msrv)] get_expectations().clear_poison(); ); quote!( /// Manages the context for expectations of static methods. /// /// Expectations on this method will be validated and cleared when /// the `Context` object drops. The `Context` object does *not* /// provide any form of synchronization, so multiple tests that set /// expectations on the same static method must provide their own. #[must_use = "Context only serves to create expectations" ] #v struct Context #ty_ig #ty_wc { // Prevent "unused type parameter" errors // Surprisingly, PhantomData is Send even if // generics are not, unlike PhantomData _phantom: ::std::marker::PhantomData< Box > } impl #ty_ig Context #ty_tg #ty_wc { /// Verify that all current expectations for this method are /// satisfied and clear them. #v fn checkpoint(&self) { Self::do_checkpoint() } #[doc(hidden)] #v fn do_checkpoint() { let __mockall_timeses = get_expectations() .lock() .unwrap() .checkpoint() .collect::>(); } /// Create a new expectation for this method. #must_use #v fn expect #meth_ig ( &self,) -> ExpectationGuard #e_tg #meth_wc { ExpectationGuard::new(get_expectations().lock().unwrap()) } } impl #ty_ig Default for Context #ty_tg #ty_wc { fn default() -> Self { Context {_phantom: std::marker::PhantomData} } } impl #ty_ig Drop for Context #ty_tg #ty_wc { fn drop(&mut self) { if ::std::thread::panicking() { // Clear poison since we're about to clear the Mutex's // contents anyway. #clear_poison // Drain all expectations so other tests can run with a // blank slate. But ignore errors so we don't // double-panic. let _ = get_expectations() .lock() .map(|mut g| g.checkpoint().collect::>()); } else { // Verify expectations are satisfied Self::do_checkpoint(); } } } ).to_tokens(tokens); } } struct Matcher<'a> { f: &'a MockFunction } impl<'a> ToTokens for Matcher<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let (ig, tg, wc) = self.f.cgenerics.split_for_impl(); let argnames = &self.f.argnames; let braces = argnames.iter() .fold(String::new(), |mut acc, _argname| { if acc.is_empty() { acc.push_str("{}"); } else { acc.push_str(", {}"); } acc }); let fn_params = &self.f.fn_params; let hrtb = self.f.hrtb(); let indices = (0..argnames.len()) .map(|i| { syn::Index::from(i) }).collect::>(); let lg = lifetimes_to_generics(&self.f.alifetimes); let pred_matches = argnames.iter().enumerate() .map(|(i, argname)| { let idx = syn::Index::from(i); quote!(__mockall_pred.#idx.eval(#argname),) }).collect::(); let preds = if self.f.concretize { quote!(()) } else { self.f.predty.iter() .map(|t| quote!(Box + Send>,)) .collect::() }; let predty = &self.f.predty; let refpredty = &self.f.refpredty; let predmatches_body = if self.f.concretize { quote!() } else { quote!(Matcher::Pred(__mockall_pred) => [#pred_matches].iter().all(|__mockall_x| *__mockall_x),) }; let preddbg_body = if self.f.concretize { quote!() } else { quote!( Matcher::Pred(__mockall_p) => { write!(__mockall_fmt, #braces, #(__mockall_p.#indices,)*) } ) }; quote!( enum Matcher #ig #wc { Always, Func(Box bool + Send>), // Version of Matcher::Func for closures that aren't Send FuncSt(::mockall::Fragile bool>>), Pred(Box<(#preds)>), // Prevent "unused type parameter" errors // Surprisingly, PhantomData is Send even if // generics are not, unlike PhantomData _Phantom(Box) } impl #ig Matcher #tg #wc { #[allow(clippy::ptr_arg)] fn matches #lg (&self, #( #argnames: &#predty, )*) -> bool { match self { Matcher::Always => true, Matcher::Func(__mockall_f) => __mockall_f(#(#argnames, )*), Matcher::FuncSt(__mockall_f) => (__mockall_f.get())(#(#argnames, )*), #predmatches_body _ => unreachable!() } } } impl #ig Default for Matcher #tg #wc { #[allow(unused_variables)] fn default() -> Self { Matcher::Always } } impl #ig ::std::fmt::Display for Matcher #tg #wc { fn fmt(&self, __mockall_fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { match self { Matcher::Always => write!(__mockall_fmt, ""), Matcher::Func(_) => write!(__mockall_fmt, ""), Matcher::FuncSt(_) => write!(__mockall_fmt, ""), #preddbg_body _ => unreachable!(), } } } ).to_tokens(tokens); } } struct RefRfunc<'a> { f: &'a MockFunction } impl<'a> ToTokens for RefRfunc<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let fn_params = &self.f.fn_params; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let owned_output = &self.f.owned_output; #[cfg(not(feature = "nightly_derive"))] let default_err_msg = "Returning default values requires the \"nightly\" feature"; #[cfg(feature = "nightly_derive")] let default_err_msg = "Can only return default values for types that impl std::Default"; quote!( enum Rfunc #ig #wc { Default(Option<#owned_output>), Const(#owned_output), // Prevent "unused type parameter" errors Surprisingly, // PhantomData is Send even if generics are not, // unlike PhantomData _Phantom(Mutex>) } impl #ig Rfunc #tg #wc { fn call #lg (&self) -> std::result::Result<&#owned_output, &'static str> { match self { Rfunc::Default(Some(ref __mockall_o)) => { ::std::result::Result::Ok(__mockall_o) }, Rfunc::Default(None) => { Err(#default_err_msg) }, Rfunc::Const(ref __mockall_o) => { ::std::result::Result::Ok(__mockall_o) }, Rfunc::_Phantom(_) => unreachable!() } } } impl #ig std::default::Default for Rfunc #tg #wc { fn default() -> Self { use ::mockall::ReturnDefault; Rfunc::Default(::mockall::DefaultReturner::<#owned_output> ::maybe_return_default()) } } ).to_tokens(tokens); } } struct RefMutRfunc<'a> { f: &'a MockFunction } impl<'a> ToTokens for RefMutRfunc<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let argnames = &self.f.argnames; let argty = &self.f.argty; let fn_params = &self.f.fn_params; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let owned_output = &self.f.owned_output; let output = &self.f.output; #[cfg(not(feature = "nightly_derive"))] let default_err_msg = "Returning default values requires the \"nightly\" feature"; #[cfg(feature = "nightly_derive")] let default_err_msg = "Can only return default values for types that impl std::Default"; quote!( #[allow(clippy::unused_unit)] enum Rfunc #ig #wc { Default(Option<#owned_output>), Mut((Box #owned_output + Send + Sync>), Option<#owned_output>), // Version of Rfunc::Mut for closures that aren't Send MutSt((::mockall::Fragile< Box #owned_output >> ), Option<#owned_output> ), Var(#owned_output), // Prevent "unused type parameter" errors Surprisingly, // PhantomData is Send even if generics are not, // unlike PhantomData _Phantom(Mutex>) } impl #ig Rfunc #tg #wc { fn call_mut #lg (&mut self, #(#argnames: #argty, )*) -> std::result::Result<#output, &'static str> { match self { Rfunc::Default(Some(ref mut __mockall_o)) => { ::std::result::Result::Ok(__mockall_o) }, Rfunc::Default(None) => { Err(#default_err_msg) }, Rfunc::Mut(ref mut __mockall_f, ref mut __mockall_o) => { *__mockall_o = Some(__mockall_f(#(#argnames, )*)); if let Some(ref mut __mockall_o2) = __mockall_o { ::std::result::Result::Ok(__mockall_o2) } else { unreachable!() } }, Rfunc::MutSt(ref mut __mockall_f, ref mut __mockall_o)=> { *__mockall_o = Some((__mockall_f.get_mut())( #(#argnames, )*) ); if let Some(ref mut __mockall_o2) = __mockall_o { ::std::result::Result::Ok(__mockall_o2) } else { unreachable!() } }, Rfunc::Var(ref mut __mockall_o) => { ::std::result::Result::Ok(__mockall_o) }, Rfunc::_Phantom(_) => unreachable!() } } } impl #ig std::default::Default for Rfunc #tg #wc { fn default() -> Self { use ::mockall::ReturnDefault; Rfunc::Default(::mockall::DefaultReturner::<#owned_output> ::maybe_return_default()) } } ).to_tokens(tokens); } } struct StaticRfunc<'a> { f: &'a MockFunction } impl<'a> ToTokens for StaticRfunc<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let argnames = &self.f.argnames; let argty = &self.f.argty; let fn_params = &self.f.fn_params; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let hrtb = self.f.hrtb(); let lg = lifetimes_to_generics(&self.f.alifetimes); let output = &self.f.output; quote!( #[allow(clippy::unused_unit)] enum Rfunc #ig #wc { Default, // Indicates that a `return_once` expectation has already // returned Expired, Mut(Box #output + Send>), // Version of Rfunc::Mut for closures that aren't Send MutSt(::mockall::Fragile< Box #output >> ), Once(Box #output + Send>), // Version of Rfunc::Once for closure that aren't Send OnceSt(::mockall::Fragile< Box #output>> ), // Prevent "unused type parameter" errors Surprisingly, // PhantomData is Send even if generics are not, // unlike PhantomData _Phantom(Box) } impl #ig Rfunc #tg #wc { fn call_mut #lg (&mut self, #( #argnames: #argty, )* ) -> std::result::Result<#output, &'static str> { match self { Rfunc::Default => { use ::mockall::ReturnDefault; ::mockall::DefaultReturner::<#output> ::return_default() }, Rfunc::Expired => { Err("called twice, but it returns by move") }, Rfunc::Mut(__mockall_f) => { ::std::result::Result::Ok(__mockall_f( #(#argnames, )* )) }, Rfunc::MutSt(__mockall_f) => { ::std::result::Result::Ok((__mockall_f.get_mut())(#(#argnames,)*)) }, Rfunc::Once(_) => { if let Rfunc::Once(mut __mockall_f) = mem::replace(self, Rfunc::Expired) { ::std::result::Result::Ok(__mockall_f( #(#argnames, )* )) } else { unreachable!() } }, Rfunc::OnceSt(_) => { if let Rfunc::OnceSt(mut __mockall_f) = mem::replace(self, Rfunc::Expired) { ::std::result::Result::Ok((__mockall_f.into_inner())(#(#argnames,)*)) } else { unreachable!() } }, Rfunc::_Phantom(_) => unreachable!() } } } impl #ig std::default::Default for Rfunc #tg #wc { fn default() -> Self { Rfunc::Default } } ).to_tokens(tokens); } } /// An expectation type for functions that take a &self and return a reference struct RefExpectation<'a> { f: &'a MockFunction } impl<'a> ToTokens for RefExpectation<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let argnames = &self.f.argnames; let argty = &self.f.argty; let common_methods = CommonExpectationMethods{f: self.f}; let desc = self.f.desc(); let funcname = self.f.funcname(); let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let (_, common_tg, _) = self.f.cgenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let output = &self.f.output; let owned_output = &self.f.owned_output; let v = &self.f.privmod_vis; quote!( /// Expectation type for methods taking a `&self` argument and /// returning immutable references. This is the type returned by /// the `expect_*` methods. #v struct Expectation #ig #wc { common: Common #common_tg, rfunc: Rfunc #tg, } #[allow(clippy::unused_unit)] impl #ig Expectation #tg #wc { /// Call this [`Expectation`] as if it were the real method. #v fn call #lg (&self, #(#argnames: #argty, )*) -> #output { use ::mockall::{ViaDebug, ViaNothing}; self.common.call(&#desc); self.rfunc.call().unwrap_or_else(|m| { let desc = std::format!( "{}", self.common.matcher.lock().unwrap()); panic!("{}: Expectation({}) {}", #funcname, desc, m); }) } /// Return a reference to a constant value from the `Expectation` #v fn return_const(&mut self, __mockall_o: #owned_output) -> &mut Self { self.rfunc = Rfunc::Const(__mockall_o); self } #common_methods } impl #ig Default for Expectation #tg #wc { fn default() -> Self { Expectation { common: Common::default(), rfunc: Rfunc::default() } } } ).to_tokens(tokens); } } /// For methods that take &mut self and return a reference struct RefMutExpectation<'a> { f: &'a MockFunction } impl<'a> ToTokens for RefMutExpectation<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let common_methods = CommonExpectationMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let desc = self.f.desc(); let funcname = self.f.funcname(); let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let (_, common_tg, _) = self.f.cgenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let owned_output = &self.f.owned_output; let v = &self.f.privmod_vis; quote!( /// Expectation type for methods taking a `&mut self` argument and /// returning references. This is the type returned by the /// `expect_*` methods. #v struct Expectation #ig #wc { common: Common #common_tg, rfunc: Rfunc #tg } #[allow(clippy::unused_unit)] impl #ig Expectation #tg #wc { /// Simulating calling the real method for this expectation #v fn call_mut #lg (&mut self, #(#argnames: #argty, )*) -> &mut #owned_output { use ::mockall::{ViaDebug, ViaNothing}; self.common.call(&#desc); let desc = std::format!( "{}", self.common.matcher.lock().unwrap()); self.rfunc.call_mut(#(#argnames, )*).unwrap_or_else(|m| { panic!("{}: Expectation({}) {}", #funcname, desc, m); }) } /// Convenience method that can be used to supply a return value /// for a `Expectation`. The value will be returned by mutable /// reference. #v fn return_var(&mut self, __mockall_o: #owned_output) -> &mut Self { self.rfunc = Rfunc::Var(__mockall_o); self } /// Supply a closure that the `Expectation` will use to create its /// return value. The return value will be returned by mutable /// reference. #v fn returning(&mut self, __mockall_f: MockallF) -> &mut Self where MockallF: FnMut(#(#argty, )*) -> #owned_output + Send + Sync + 'static { self.rfunc = Rfunc::Mut(Box::new(__mockall_f), None); self } /// Single-threaded version of [`returning`](#method.returning). /// Can be used when the argument or return type isn't `Send`. #v fn returning_st(&mut self, __mockall_f: MockallF) -> &mut Self where MockallF: FnMut(#(#argty, )*) -> #owned_output + 'static { self.rfunc = Rfunc::MutSt( ::mockall::Fragile::new(Box::new(__mockall_f)), None); self } #common_methods } impl #ig Default for Expectation #tg #wc { fn default() -> Self { Expectation { common: Common::default(), rfunc: Rfunc::default() } } } ).to_tokens(tokens); } } /// An expectation type for functions return a `'static` value struct StaticExpectation<'a> { f: &'a MockFunction } impl<'a> ToTokens for StaticExpectation<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let common_methods = CommonExpectationMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let desc = self.f.desc(); let hrtb = self.f.hrtb(); let funcname = self.f.funcname(); let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let (_, common_tg, _) = self.f.cgenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let output = &self.f.output; let v = &self.f.privmod_vis; quote!( /// Expectation type for methods that return a `'static` type. /// This is the type returned by the `expect_*` methods. #v struct Expectation #ig #wc { common: Common #common_tg, rfunc: Mutex, } #[allow(clippy::unused_unit)] impl #ig Expectation #tg #wc { /// Call this [`Expectation`] as if it were the real method. #[doc(hidden)] #v fn call #lg (&self, #(#argnames: #argty, )* ) -> #output { use ::mockall::{ViaDebug, ViaNothing}; self.common.call(&#desc); self.rfunc.lock().unwrap().call_mut(#(#argnames, )*) .unwrap_or_else(|message| { let desc = std::format!( "{}", self.common.matcher.lock().unwrap()); panic!("{}: Expectation({}) {}", #funcname, desc, message); }) } /// Return a constant value from the `Expectation` /// /// The output type must be `Clone`. The compiler can't always /// infer the proper type to use with this method; you will /// usually need to specify it explicitly. i.e. /// `return_const(42i32)` instead of `return_const(42)`. // We must use Into<#output> instead of #output because where // clauses don't accept equality constraints. // https://github.com/rust-lang/rust/issues/20041 #[allow(unused_variables)] #v fn return_const(&mut self, __mockall_c: MockallOutput) -> &mut Self where MockallOutput: Clone + Into<#output> + Send + 'static { self.returning(move |#(#argnames, )*| __mockall_c.clone().into()) } /// Single-threaded version of /// [`return_const`](#method.return_const). This is useful for /// return types that are not `Send`. /// /// The output type must be `Clone`. The compiler can't always /// infer the proper type to use with this method; you will /// usually need to specify it explicitly. i.e. /// `return_const(42i32)` instead of `return_const(42)`. /// /// It is a runtime error to call the mock method from a /// different thread than the one that originally called this /// method. // We must use Into<#output> instead of #output because where // clauses don't accept equality constraints. // https://github.com/rust-lang/rust/issues/20041 #[allow(unused_variables)] #v fn return_const_st(&mut self, __mockall_c: MockallOutput) -> &mut Self where MockallOutput: Clone + Into<#output> + 'static { self.returning_st(move |#(#argnames, )*| __mockall_c.clone().into()) } /// Supply an `FnOnce` closure that will provide the return /// value for this Expectation. This is useful for return types /// that aren't `Clone`. It will be an error to call this /// method multiple times. #v fn return_once(&mut self, __mockall_f: MockallF) -> &mut Self where MockallF: #hrtb FnOnce(#(#argty, )*) -> #output + Send + 'static { { let mut __mockall_guard = self.rfunc.lock().unwrap(); *__mockall_guard.deref_mut() = Rfunc::Once(Box::new(__mockall_f)); } self } /// Single-threaded version of /// [`return_once`](#method.return_once). This is useful for /// return types that are neither `Send` nor `Clone`. /// /// It is a runtime error to call the mock method from a /// different thread than the one that originally called this /// method. It is also a runtime error to call the method more /// than once. #v fn return_once_st(&mut self, __mockall_f: MockallF) -> &mut Self where MockallF: #hrtb FnOnce(#(#argty, )*) -> #output + 'static { { let mut __mockall_guard = self.rfunc.lock().unwrap(); *__mockall_guard.deref_mut() = Rfunc::OnceSt( ::mockall::Fragile::new(Box::new(__mockall_f))); } self } /// Supply a closure that will provide the return value for this /// `Expectation`. The method's arguments are passed to the /// closure by value. #v fn returning(&mut self, __mockall_f: MockallF) -> &mut Self where MockallF: #hrtb FnMut(#(#argty, )*) -> #output + Send + 'static { { let mut __mockall_guard = self.rfunc.lock().unwrap(); *__mockall_guard.deref_mut() = Rfunc::Mut(Box::new(__mockall_f)); } self } /// Single-threaded version of [`returning`](#method.returning). /// Can be used when the argument or return type isn't `Send`. /// /// It is a runtime error to call the mock method from a /// different thread than the one that originally called this /// method. #v fn returning_st(&mut self, __mockall_f: MockallF) -> &mut Self where MockallF: #hrtb FnMut(#(#argty, )*) -> #output + 'static { { let mut __mockall_guard = self.rfunc.lock().unwrap(); *__mockall_guard.deref_mut() = Rfunc::MutSt( ::mockall::Fragile::new(Box::new(__mockall_f))); } self } #common_methods } impl #ig Default for Expectation #tg #wc { fn default() -> Self { Expectation { common: Common::default(), rfunc: Mutex::new(Rfunc::default()) } } } ).to_tokens(tokens); } } /// An collection of RefExpectation's struct RefExpectations<'a> { f: &'a MockFunction } impl<'a> ToTokens for RefExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let common_methods = CommonExpectationsMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let output = &self.f.output; let predexprs = &self.f.predexprs; let v = &self.f.privmod_vis; quote!( #common_methods impl #ig Expectations #tg #wc { /// Simulate calling the real method. Every current expectation /// will be checked in FIFO order and the first one with /// matching arguments will be used. #v fn call #lg (&self, #(#argnames: #argty, )* ) -> Option<#output> { self.0.iter() .find(|__mockall_e| __mockall_e.matches(#(#predexprs, )*) && (!__mockall_e.is_done() || self.0.len() == 1)) .map(move |__mockall_e| __mockall_e.call(#(#argnames),*) ) } } ).to_tokens(tokens); } } /// An collection of RefMutExpectation's struct RefMutExpectations<'a> { f: &'a MockFunction } impl<'a> ToTokens for RefMutExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let common_methods = CommonExpectationsMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let output = &self.f.output; let predexprs = &self.f.predexprs; let v = &self.f.privmod_vis; quote!( #common_methods impl #ig Expectations #tg #wc { /// Simulate calling the real method. Every current expectation /// will be checked in FIFO order and the first one with /// matching arguments will be used. #v fn call_mut #lg (&mut self, #(#argnames: #argty, )* ) -> Option<#output> { let __mockall_n = self.0.len(); self.0.iter_mut() .find(|__mockall_e| __mockall_e.matches(#(#predexprs, )*) && (!__mockall_e.is_done() || __mockall_n == 1)) .map(move |__mockall_e| __mockall_e.call_mut(#(#argnames, )*) ) } } ).to_tokens(tokens); } } /// An collection of Expectation's for methods returning static values struct StaticExpectations<'a> { f: &'a MockFunction } impl<'a> ToTokens for StaticExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let common_methods = CommonExpectationsMethods{f: self.f}; let argnames = &self.f.argnames; let argty = &self.f.argty; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let lg = lifetimes_to_generics(&self.f.alifetimes); let output = &self.f.output; let predexprs = &self.f.predexprs; let v = &self.f.privmod_vis; quote!( #common_methods impl #ig Expectations #tg #wc { /// Simulate calling the real method. Every current expectation /// will be checked in FIFO order and the first one with /// matching arguments will be used. #v fn call #lg (&self, #(#argnames: #argty, )* ) -> Option<#output> { self.0.iter() .find(|__mockall_e| __mockall_e.matches(#(#predexprs, )*) && (!__mockall_e.is_done() || self.0.len() == 1)) .map(move |__mockall_e| __mockall_e.call(#(#argnames, )*) ) } } ).to_tokens(tokens); } } struct GenericExpectations<'a> { f: &'a MockFunction } impl<'a> ToTokens for GenericExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { if ! self.f.is_expectation_generic() { return; } if ! self.f.is_static() && ! self.f.is_method_generic() { return; } let ge = StaticGenericExpectations{f: self.f}; let v = &self.f.privmod_vis; quote!( /// A collection of [`Expectation`](struct.Expectations.html) /// objects for a generic method. Users will rarely if ever use /// this struct directly. #[doc(hidden)] #[derive(Default)] #v struct GenericExpectations{ store: std::collections::hash_map::HashMap<::mockall::Key, Box> } impl GenericExpectations { /// Verify that all current expectations are satisfied and clear /// them. This applies to all sets of generic parameters! #v fn checkpoint(&mut self) -> std::collections::hash_map::Drain<::mockall::Key, Box> { self.store.drain() } #v fn new() -> Self { Self::default() } } #ge ).to_tokens(tokens); } } /// Generates methods for GenericExpectations for methods returning static /// values struct StaticGenericExpectations<'a> { f: &'a MockFunction } impl<'a> ToTokens for StaticGenericExpectations<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let argnames = &self.f.argnames; let argty = &self.f.argty; let (ig, tg, wc) = self.f.egenerics.split_for_impl(); let keyid = gen_keyid(&self.f.egenerics); let mut any_wc = wc.cloned(); if self.f.return_ref || self.f.return_refmut { // Add Senc + Sync, required for downcast, since Expectation // stores an Option<#owned_output> send_syncify(&mut any_wc, self.f.owned_output.clone()); } let tbf = tg.as_turbofish(); let output = &self.f.output; let v = &self.f.privmod_vis; let (call, get, self_, downcast) = if self.f.return_refmut { (format_ident!("call_mut"), format_ident!("get_mut"), quote!(&mut self), format_ident!("downcast_mut")) } else { (format_ident!("call"), format_ident!("get"), quote!(&self), format_ident!("downcast_ref")) }; quote!( impl #ig ::mockall::AnyExpectations for Expectations #tg #any_wc {} impl GenericExpectations { /// Simulating calling the real method. #v fn #call #ig (#self_, #(#argnames: #argty, )* ) -> Option<#output> #wc { self.store.#get(&::mockall::Key::new::#keyid()) .map(|__mockall_e| { __mockall_e.#downcast::() .unwrap() .#call(#(#argnames, )*) }).flatten() } /// Create a new Expectation. #v fn expect #ig (&mut self) -> &mut Expectation #tg #any_wc { self.store.entry(::mockall::Key::new::#keyid()) .or_insert_with(|| Box::new(Expectations #tbf::new())) .downcast_mut::() .unwrap() .expect() } } ).to_tokens(tokens) } } mockall_derive-0.13.0/src/mock_item.rs000064400000000000000000000165371046102023000160050ustar 00000000000000// vim: tw=80 use proc_macro2::{TokenStream}; use quote::{ToTokens, quote}; use syn::{ *, spanned::Spanned }; use crate::{ MockItemStruct, compile_error, mock_function::{self, MockFunction}, mockable_item::{MockableItem, MockableModule} }; /// A Mock item pub(crate) enum MockItem { Module(MockItemModule), Struct(MockItemStruct) } impl From for MockItem { fn from(mockable: MockableItem) -> MockItem { match mockable { MockableItem::Struct(s) => MockItem::Struct( MockItemStruct::from(s) ), MockableItem::Module(mod_) => MockItem::Module( MockItemModule::from(mod_) ) } } } impl ToTokens for MockItem { fn to_tokens(&self, tokens: &mut TokenStream) { match self { MockItem::Module(mod_) => mod_.to_tokens(tokens), MockItem::Struct(s) => s.to_tokens(tokens) } } } enum MockItemContent { Fn(Box), Tokens(TokenStream) } pub(crate) struct MockItemModule { attrs: TokenStream, vis: Visibility, mock_ident: Ident, orig_ident: Option, content: Vec } impl From for MockItemModule { fn from(mod_: MockableModule) -> MockItemModule { let mock_ident = mod_.mock_ident.clone(); let orig_ident = mod_.orig_ident; let mut content = Vec::new(); for item in mod_.content.into_iter() { let span = item.span(); match item { Item::ExternCrate(_) | Item::Impl(_) => { // Ignore }, Item::Static(is) => { content.push( MockItemContent::Tokens(is.into_token_stream()) ); }, Item::Const(ic) => { content.push( MockItemContent::Tokens(ic.into_token_stream()) ); }, Item::Fn(f) => { let mf = mock_function::Builder::new(&f.sig, &f.vis) .attrs(&f.attrs) .parent(&mock_ident) .levels(1) .call_levels(0) .build(); content.push(MockItemContent::Fn(Box::new(mf))); }, Item::ForeignMod(ifm) => { for item in ifm.items { if let ForeignItem::Fn(mut f) = item { // Foreign functions are always unsafe. Mock // foreign functions should be unsafe too, to // prevent "warning: unused unsafe" messages. f.sig.unsafety = Some(Token![unsafe](f.span())); // Set the ABI to match the ForeignMod's ABI // for proper function linkage with external code. // // BUT, use C-unwind instead of C, so we can panic // from the mock function (rust-lang/rust #74990) let needs_c_unwind = if let Some(n) = &ifm.abi.name { n.value() == "C" } else { false }; f.sig.abi = Some(if needs_c_unwind { Abi { extern_token:ifm.abi.extern_token, name: Some(LitStr::new("C-unwind", ifm.abi.name.span())) } } else { ifm.abi.clone() }); let mf = mock_function::Builder::new(&f.sig, &f.vis) .attrs(&f.attrs) .parent(&mock_ident) .levels(1) .call_levels(0) .build(); content.push(MockItemContent::Fn(Box::new(mf))); } else { compile_error(item.span(), "Mockall does not yet support this type in this position. Please open an issue with your use case at https://github.com/asomers/mockall"); } } }, Item::Mod(_) | Item::Struct(_) | Item::Enum(_) | Item::Union(_) | Item::Trait(_) => { compile_error(span, "Mockall does not yet support deriving nested mocks"); }, Item::Type(ty) => { content.push( MockItemContent::Tokens(ty.into_token_stream()) ); }, Item::TraitAlias(ta) => { content.push (MockItemContent::Tokens(ta.into_token_stream()) ); }, Item::Use(u) => { content.push( MockItemContent::Tokens(u.into_token_stream()) ); }, _ => compile_error(span, "Unsupported item") } } MockItemModule { attrs: mod_.attrs, vis: mod_.vis, mock_ident: mod_.mock_ident, orig_ident, content } } } impl ToTokens for MockItemModule { fn to_tokens(&self, tokens: &mut TokenStream) { let mut body = TokenStream::new(); let mut cp_body = TokenStream::new(); let attrs = &self.attrs; let modname = &self.mock_ident; let vis = &self.vis; for item in self.content.iter() { match item { MockItemContent::Tokens(ts) => ts.to_tokens(&mut body), MockItemContent::Fn(f) => { let call = f.call(None); let ctx_fn = f.context_fn(None); let priv_mod = f.priv_module(); quote!( #priv_mod #call #ctx_fn ).to_tokens(&mut body); f.checkpoint().to_tokens(&mut cp_body); }, } } quote!( /// Verify that all current expectations for every function in /// this module are satisfied and clear them. pub fn checkpoint() { #cp_body } ).to_tokens(&mut body); let docstr = { if let Some(ident) = &self.orig_ident { let inner = format!("Mock version of the `{ident}` module"); quote!( #[doc = #inner]) } else { // Typically an extern FFI block. Not really anything good we // can put in the doc string. quote!(#[allow(missing_docs)]) } }; quote!( #[allow(unused_imports)] #attrs #docstr #vis mod #modname { #body }).to_tokens(tokens); } } mockall_derive-0.13.0/src/mock_item_struct.rs000064400000000000000000000361151046102023000174030ustar 00000000000000// vim: tw=80 use std::collections::HashSet; use proc_macro2::TokenStream; use quote::{ToTokens, format_ident, quote}; use syn::{ *, spanned::Spanned }; use crate::{ AttrFormatter, MockableStruct, compile_error, gen_mod_ident, mock_function::{self, MockFunction}, mock_trait::MockTrait }; fn phantom_default_inits(generics: &Generics) -> Vec { generics.params .iter() .enumerate() .map(|(count, _param)| { let phident = format_ident!("_t{count}"); quote!(#phident: ::std::marker::PhantomData) }).collect() } /// Generate any PhantomData field definitions fn phantom_fields(generics: &Generics) -> Vec { generics.params .iter() .enumerate() .filter_map(|(count, param)| { let phident = format_ident!("_t{count}"); match param { syn::GenericParam::Lifetime(l) => { if !l.bounds.is_empty() { compile_error(l.bounds.span(), "#automock does not yet support lifetime bounds on structs"); } let lifetime = &l.lifetime; Some( quote!(#phident: ::std::marker::PhantomData<&#lifetime ()>) ) }, syn::GenericParam::Type(tp) => { let ty = &tp.ident; Some( quote!(#phident: ::std::marker::PhantomData<#ty>) ) }, syn::GenericParam::Const(_) => { compile_error(param.span(), "#automock does not yet support generic constants"); None } } }).collect() } /// Filter out multiple copies of the same trait, even if they're implemented on /// different types. But allow them if they have different attributes, which /// probably indicates that they aren't meant to be compiled together. fn unique_trait_iter<'a, I: Iterator>(i: I) -> impl Iterator { let mut hs = HashSet::<(Path, Vec)>::default(); i.filter(move |mt| { let impl_attrs = AttrFormatter::new(&mt.attrs) .async_trait(false) .doc(false) .format(); let key = (mt.trait_path.clone(), impl_attrs); if hs.contains(&key) { false } else { hs.insert(key); true } }) } /// A collection of methods defined in one spot struct Methods(Vec); impl Methods { /// Are all of these methods static? fn all_static(&self) -> bool { self.0.iter() .all(|meth| meth.is_static()) } fn checkpoints(&self) -> Vec { self.0.iter() .filter(|meth| !meth.is_static()) .map(|meth| meth.checkpoint()) .collect::>() } /// Return a fragment of code to initialize struct fields during default() fn default_inits(&self) -> Vec { self.0.iter() .filter(|meth| !meth.is_static()) .map(|meth| { let name = meth.name(); let attrs = AttrFormatter::new(&meth.attrs) .doc(false) .format(); quote!(#(#attrs)* #name: Default::default()) }).collect::>() } fn field_definitions(&self, modname: &Ident) -> Vec { self.0.iter() .filter(|meth| !meth.is_static()) .map(|meth| meth.field_definition(Some(modname))) .collect::>() } fn priv_mods(&self) -> Vec { self.0.iter() .map(|meth| meth.priv_module()) .collect::>() } } pub(crate) struct MockItemStruct { attrs: Vec, consts: Vec, generics: Generics, /// Should Mockall generate a Debug implementation? auto_debug: bool, /// Does the original struct have a `new` method? has_new: bool, /// Inherent methods of the mock struct methods: Methods, /// Name of the overall module that holds all of the mock stuff modname: Ident, name: Ident, /// Is this a whole MockStruct or just a substructure for a trait impl? traits: Vec, vis: Visibility, } impl MockItemStruct { fn debug_impl(&self) -> impl ToTokens { if self.auto_debug { let (ig, tg, wc) = self.generics.split_for_impl(); let struct_name = &self.name; let struct_name_str = format!("{}", self.name); quote!( impl #ig ::std::fmt::Debug for #struct_name #tg #wc { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), std::fmt::Error> { f.debug_struct(#struct_name_str).finish() } } ) } else { quote!() } } fn new_method(&self) -> impl ToTokens { if self.has_new { TokenStream::new() } else { quote!( /// Create a new mock object with no expectations. /// /// This method will not be generated if the real struct /// already has a `new` method. However, it *will* be /// generated if the struct implements a trait with a `new` /// method. The trait's `new` method can still be called /// like `::new` pub fn new() -> Self { Self::default() } ) } } fn phantom_default_inits(&self) -> Vec { phantom_default_inits(&self.generics) } fn phantom_fields(&self) -> Vec { phantom_fields(&self.generics) } } impl From for MockItemStruct { fn from(mockable: MockableStruct) -> MockItemStruct { let auto_debug = mockable.derives_debug(); let modname = gen_mod_ident(&mockable.name, None); let generics = mockable.generics.clone(); let struct_name = &mockable.name; let vis = mockable.vis; let has_new = mockable.methods.iter() .any(|meth| meth.sig.ident == "new") || mockable.impls.iter() .any(|impl_| impl_.items.iter() .any(|ii| if let ImplItem::Fn(iif) = ii { iif.sig.ident == "new" } else { false } ) ); let methods = Methods(mockable.methods.into_iter() .map(|meth| mock_function::Builder::new(&meth.sig, &meth.vis) .attrs(&meth.attrs) .struct_(struct_name) .struct_generics(&generics) .levels(2) .call_levels(0) .build() ).collect::>()); let structname = &mockable.name; let traits = mockable.impls.into_iter() .map(|i| MockTrait::new(structname, &generics, i, &vis)) .collect(); MockItemStruct { attrs: mockable.attrs, auto_debug, consts: mockable.consts, generics, has_new, methods, modname, name: mockable.name, traits, vis } } } impl ToTokens for MockItemStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let attrs = AttrFormatter::new(&self.attrs) .async_trait(false) .must_use(true) .format(); let consts = &self.consts; let debug_impl = self.debug_impl(); let struct_name = &self.name; let (ig, tg, wc) = self.generics.split_for_impl(); let modname = &self.modname; let calls = self.methods.0.iter() .map(|meth| meth.call(Some(modname))) .collect::>(); let contexts = self.methods.0.iter() .filter(|meth| meth.is_static()) .map(|meth| meth.context_fn(Some(modname))) .collect::>(); let expects = self.methods.0.iter() .filter(|meth| !meth.is_static()) .map(|meth| meth.expect(modname, None)) .collect::>(); let method_checkpoints = self.methods.checkpoints(); let new_method = self.new_method(); let priv_mods = self.methods.priv_mods(); let substructs = unique_trait_iter(self.traits.iter()) .map(|trait_| { MockItemTraitImpl { attrs: trait_.attrs.clone(), generics: self.generics.clone(), fieldname: format_ident!("{}_expectations", trait_.ss_name()), methods: Methods(trait_.methods.clone()), modname: format_ident!("{}_{}", &self.modname, trait_.ss_name()), name: format_ident!("{}_{}", &self.name, trait_.ss_name()), } }).collect::>(); let substruct_expectations = substructs.iter() .filter(|ss| !ss.all_static()) .map(|ss| { let attrs = AttrFormatter::new(&ss.attrs) .async_trait(false) .doc(false) .format(); let fieldname = &ss.fieldname; quote!(#(#attrs)* self.#fieldname.checkpoint();) }).collect::>(); let mut field_definitions = substructs.iter() .filter(|ss| !ss.all_static()) .map(|ss| { let attrs = AttrFormatter::new(&ss.attrs) .async_trait(false) .doc(false) .format(); let fieldname = &ss.fieldname; let tyname = &ss.name; quote!(#(#attrs)* #fieldname: #tyname #tg) }).collect::>(); field_definitions.extend(self.methods.field_definitions(modname)); field_definitions.extend(self.phantom_fields()); let mut default_inits = substructs.iter() .filter(|ss| !ss.all_static()) .map(|ss| { let attrs = AttrFormatter::new(&ss.attrs) .async_trait(false) .doc(false) .format(); let fieldname = &ss.fieldname; quote!(#(#attrs)* #fieldname: Default::default()) }).collect::>(); default_inits.extend(self.methods.default_inits()); default_inits.extend(self.phantom_default_inits()); let trait_impls = self.traits.iter() .map(|trait_| { let modname = format_ident!("{}_{}", &self.modname, trait_.ss_name()); trait_.trait_impl(&modname) }).collect::>(); let vis = &self.vis; quote!( #[allow(non_snake_case)] #[allow(missing_docs)] pub mod #modname { use super::*; #(#priv_mods)* } #[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(missing_docs)] #(#attrs)* #vis struct #struct_name #ig #wc { #(#field_definitions),* } #debug_impl impl #ig ::std::default::Default for #struct_name #tg #wc { #[allow(clippy::default_trait_access)] fn default() -> Self { Self { #(#default_inits),* } } } #(#substructs)* impl #ig #struct_name #tg #wc { #(#consts)* #(#calls)* #(#contexts)* #(#expects)* /// Validate that all current expectations for all methods have /// been satisfied, and discard them. pub fn checkpoint(&mut self) { #(#substruct_expectations)* #(#method_checkpoints)* } #new_method } #(#trait_impls)* ).to_tokens(tokens); } } pub(crate) struct MockItemTraitImpl { attrs: Vec, generics: Generics, /// Inherent methods of the mock struct methods: Methods, /// Name of the overall module that holds all of the mock stuff modname: Ident, name: Ident, /// Name of the field of this type in the parent's structure fieldname: Ident, } impl MockItemTraitImpl { /// Are all of this traits's methods static? fn all_static(&self) -> bool { self.methods.all_static() } fn phantom_default_inits(&self) -> Vec { phantom_default_inits(&self.generics) } fn phantom_fields(&self) -> Vec { phantom_fields(&self.generics) } } impl ToTokens for MockItemTraitImpl { fn to_tokens(&self, tokens: &mut TokenStream) { let mod_attrs = AttrFormatter::new(&self.attrs) .async_trait(false) .doc(false) .format(); let struct_attrs = AttrFormatter::new(&self.attrs) .async_trait(false) .doc(false) .must_use(false) .format(); let impl_attrs = AttrFormatter::new(&self.attrs) .async_trait(false) .doc(false) .format(); let struct_name = &self.name; let (ig, tg, wc) = self.generics.split_for_impl(); let modname = &self.modname; let method_checkpoints = self.methods.checkpoints(); let mut default_inits = self.methods.default_inits(); default_inits.extend(self.phantom_default_inits()); let mut field_definitions = self.methods.field_definitions(modname); field_definitions.extend(self.phantom_fields()); let priv_mods = self.methods.priv_mods(); quote!( #[allow(non_snake_case)] #[allow(missing_docs)] #(#mod_attrs)* pub mod #modname { use super::*; #(#priv_mods)* } #[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(missing_docs)] #(#struct_attrs)* struct #struct_name #ig #wc { #(#field_definitions),* } #(#impl_attrs)* impl #ig ::std::default::Default for #struct_name #tg #wc { fn default() -> Self { Self { #(#default_inits),* } } } #(#impl_attrs)* impl #ig #struct_name #tg #wc { /// Validate that all current expectations for all methods have /// been satisfied, and discard them. pub fn checkpoint(&mut self) { #(#method_checkpoints)* } } ).to_tokens(tokens); } } mockall_derive-0.13.0/src/mock_trait.rs000064400000000000000000000136441046102023000161660ustar 00000000000000// vim: tw=80 use proc_macro2::Span; use quote::{ToTokens, format_ident, quote}; use std::{ collections::hash_map::DefaultHasher, hash::{Hash, Hasher} }; use syn::{ *, spanned::Spanned }; use crate::{ AttrFormatter, mock_function::{self, MockFunction}, compile_error }; pub(crate) struct MockTrait { pub attrs: Vec, pub consts: Vec, pub generics: Generics, pub methods: Vec, /// Internally-used name of the trait used. pub ss_name: Ident, /// Fully-qualified name of the trait pub trait_path: Path, /// Path on which the trait is implemented. Usually will be the same as /// structname, but might include concrete generic parameters. self_path: PathSegment, pub types: Vec, pub unsafety: Option } impl MockTrait { fn ss_name_priv(trait_path: &Path) -> Ident { let path_args = &trait_path.segments.last().unwrap().arguments; if path_args.is_empty() { // Skip the hashing step for easie debugging of generated code format_ident!("{}", trait_path.segments.last().unwrap().ident) } else { // Hash the path args to permit mocking structs that implement // multiple traits distinguished only by their path args let mut hasher = DefaultHasher::new(); path_args.hash(&mut hasher); format_ident!("{}_{}", trait_path.segments.last().unwrap().ident, hasher.finish()) } } pub fn ss_name(&self) -> &Ident { &self.ss_name } /// Create a new MockTrait /// /// # Arguments /// * `structname` - name of the struct that implements this trait /// * `struct_generics` - Generics of the parent structure /// * `impl_` - Mockable ItemImpl for a trait /// * `vis` - Visibility of the struct pub fn new(structname: &Ident, struct_generics: &Generics, impl_: ItemImpl, vis: &Visibility) -> Self { let mut consts = Vec::new(); let mut methods = Vec::new(); let mut types = Vec::new(); let trait_path = if let Some((_, path, _)) = impl_.trait_ { path } else { compile_error(impl_.span(), "impl block must implement a trait"); Path::from(format_ident!("__mockall_invalid")) }; let ss_name = MockTrait::ss_name_priv(&trait_path); let self_path = match *impl_.self_ty { Type::Path(mut type_path) => type_path.path.segments.pop().unwrap().into_value(), x => { compile_error(x.span(), "mockall_derive only supports mocking traits and structs"); PathSegment::from(Ident::new("", Span::call_site())) } }; for ii in impl_.items.into_iter() { match ii { ImplItem::Const(iic) => { consts.push(iic); }, ImplItem::Fn(iif) => { let mf = mock_function::Builder::new(&iif.sig, vis) .attrs(&iif.attrs) .levels(2) .call_levels(0) .struct_(structname) .struct_generics(struct_generics) .trait_(&ss_name) .build(); methods.push(mf); }, ImplItem::Type(iit) => { types.push(iit); }, _ => { compile_error(ii.span(), "This impl item is not yet supported by MockAll"); } } } MockTrait { attrs: impl_.attrs, consts, generics: impl_.generics, methods, ss_name, trait_path, self_path, types, unsafety: impl_.unsafety } } /// Generate code for the trait implementation on the mock struct /// /// # Arguments /// /// * `modname`: Name of the parent struct's private module // Supplying modname is an unfortunately hack. Ideally MockTrait // wouldn't need to know that. pub fn trait_impl(&self, modname: &Ident) -> impl ToTokens { let trait_impl_attrs = AttrFormatter::new(&self.attrs) .must_use(false) .format(); let impl_attrs = AttrFormatter::new(&self.attrs) .async_trait(false) .doc(false) .format(); let (ig, _tg, wc) = self.generics.split_for_impl(); let consts = &self.consts; let path_args = &self.self_path.arguments; let calls = self.methods.iter() .map(|meth| meth.call(Some(modname))) .collect::>(); let contexts = self.methods.iter() .filter(|meth| meth.is_static()) .map(|meth| meth.context_fn(Some(modname))) .collect::>(); let expects = self.methods.iter() .filter(|meth| !meth.is_static()) .map(|meth| { if meth.is_method_generic() { // Specific impls with generic methods are TODO. meth.expect(modname, None) } else { meth.expect(modname, Some(path_args)) } }).collect::>(); let trait_path = &self.trait_path; let self_path = &self.self_path; let types = &self.types; let unsafety = &self.unsafety; quote!( #(#trait_impl_attrs)* #unsafety impl #ig #trait_path for #self_path #wc { #(#consts)* #(#types)* #(#calls)* } #(#impl_attrs)* impl #ig #self_path #wc { #(#expects)* #(#contexts)* } ) } } mockall_derive-0.13.0/src/mockable_item.rs000064400000000000000000000045231046102023000166210ustar 00000000000000// vim: tw=80 use super::*; /// Performs transformations on a function to make it mockable fn mockable_fn(mut item_fn: ItemFn) -> ItemFn { demutify(&mut item_fn.sig.inputs); deimplify(&mut item_fn.sig.output); item_fn } /// Performs transformations on an Item to make it mockable fn mockable_item(item: Item) -> Item { match item { Item::Fn(item_fn) => Item::Fn(mockable_fn(item_fn)), x => x } } /// An item that's ready to be mocked. /// /// It should be functionally identical or near-identical to the original item, /// but with minor alterations that make it suitable for mocking, such as /// altered lifetimes. pub(crate) enum MockableItem { Module(MockableModule), Struct(MockableStruct) } impl From<(Attrs, Item)> for MockableItem { fn from((attrs, item): (Attrs, Item)) -> MockableItem { match item { Item::Impl(item_impl) => MockableItem::Struct(MockableStruct::from(item_impl)), Item::Mod(item_mod) => MockableItem::Module(MockableModule::from(item_mod)), Item::Trait(trait_) => MockableItem::Struct(MockableStruct::from((attrs, trait_))), _ => panic!("automock does not support this item type") } } } impl From for MockableItem { fn from(mock: MockableStruct) -> MockableItem { MockableItem::Struct(mock) } } pub(crate) struct MockableModule { pub attrs: TokenStream, pub vis: Visibility, pub mock_ident: Ident, /// Ident of the original module, if any pub orig_ident: Option, pub content: Vec } impl From for MockableModule { fn from(mod_: ItemMod) -> MockableModule { let span = mod_.span(); let vis = mod_.vis; let mock_ident = format_ident!("mock_{}", mod_.ident); let orig_ident = Some(mod_.ident); let content = if let Some((_, content)) = mod_.content { content.into_iter() .map(mockable_item) .collect() } else { compile_error(span, "automock can only mock inline modules, not modules from another file"); Vec::new() }; MockableModule { attrs: TokenStream::new(), vis, mock_ident, orig_ident, content } } } mockall_derive-0.13.0/src/mockable_struct.rs000064400000000000000000000523731046102023000172150ustar 00000000000000// vim: tw=80 use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{ *, parse::{Parse, ParseStream}, spanned::Spanned }; use crate::{ Attrs, compile_error, deanonymize, deimplify, demutify, deselfify, deselfify_args, dewhereselfify, find_ident_from_path, gen_mock_ident, AttrFormatter, }; /// Make any implicit lifetime parameters explicit fn add_lifetime_parameters(sig: &mut Signature) { fn add_to_trait_object(generics: &mut Generics, var: &Pat, to: &mut TypeTraitObject) { let mut has_lifetime = false; for bound in to.bounds.iter() { if let 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) => { if let Some(r) = &pat_ident.by_ref { compile_error(r.span(), "Mockall does not support by-reference argument bindings"); } if let 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![<](span)); generics.gt_token.get_or_insert(Token![>](span)); let gpl = GenericParam::Lifetime(LifetimeParam::new(lt)); generics.params.push(gpl); } } fn add_to_type(generics: &mut Generics, var: &Pat, ty: &mut Type) { 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) => { if let Type::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() { if let 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: ::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: &mut Type) { 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() { if let ImplItem::Fn(ref mut 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); if let 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); if let ReturnType::Type(_, ty) = &mut meth.sig.output { deselfify(ty, name, generics); deanonymize(ty); } sanity_check_sig(&meth.sig); } /// Generates a mockable item impl from a trait method definition fn mockable_trait(trait_: ItemTrait, name: &Ident, generics: &Generics) -> ItemImpl { let items = trait_.items.into_iter() .map(|ti| { match ti { TraitItem::Fn(mut tif) => { mockable_trait_method(&mut tif, name, generics); ImplItem::Fn(tif2iif(tif, &Visibility::Inherited)) }, TraitItem::Const(tic) => { ImplItem::Const(tic2iic(tic, &Visibility::Inherited)) }, TraitItem::Type(tit) => { ImplItem::Type(tit2iit(tit, &Visibility::Inherited)) }, _ => { compile_error(ti.span(), "Unsupported in this context"); ImplItem::Verbatim(TokenStream::new()) } } }).collect::>(); let mut trait_path = Path::from(trait_.ident); let mut struct_path = Path::from(name.clone()); let (_, stg, _) = generics.split_for_impl(); let (_, ttg, _) = trait_.generics.split_for_impl(); if let Ok(abga) = parse2::(quote!(#stg)) { struct_path.segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(abga); } if let Ok(abga) = parse2::(quote!(#ttg)) { trait_path.segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(abga); } let self_ty = Box::new(Type::Path(TypePath{ qself: None, path: struct_path, })); ItemImpl { attrs: trait_.attrs, defaultness: None, unsafety: trait_.unsafety, impl_token: ::default(), generics: generics.clone(), trait_: Some((None, trait_path, ::default())), self_ty, brace_token: trait_.brace_token, items } } fn sanity_check_sig(sig: &Signature) { for arg in sig.inputs.iter() { if let FnArg::Typed(pt) = arg { if let Type::ImplTrait(it) = pt.ty.as_ref() { let bounds = &it.bounds; let s = format!( "Mockall does not support \"impl trait\" 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"); (::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 Parse for TraitItemVFn { fn parse(input: ParseStream) -> syn::parse::Result { let attrs = input.call(Attribute::parse_outer)?; let vis: syn::Visibility = input.parse()?; let mut tif: TraitItemFn = input.parse()?; tif.attrs = attrs; Ok(Self{vis, tif}) } } pub(crate) struct MockableStruct { pub attrs: Vec, pub consts: Vec, pub generics: Generics, /// Inherent methods of the mockable struct pub methods: Vec, pub name: Ident, pub vis: Visibility, pub impls: Vec, } impl MockableStruct { /// Does this struct derive Debug? pub fn derives_debug(&self) -> bool { self.attrs.iter() .any(|attr|{ let mut 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); // Strip "must_use" from a trait definition. For traits, the "must_use" // should apply only when the trait is used like "impl Trait" or "dyn // Trait". So it shouldn't necessarily affect the mock struct that // implements the trait. let mut attrs = AttrFormatter::new(&trait_.attrs) .doc(true) .async_trait(true) .must_use(false) .format(); 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 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()) } }; let mut attrs = item_impl.attrs.clone(); attrs.push(derive_debug()); let mut consts = Vec::new(); let generics = item_impl.generics.clone(); let mut methods = Vec::new(); let vis = Visibility::Public(Token![pub](Span::call_site())); let mut impls = Vec::new(); if let 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. `::MyType` let mut 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 { let attrs = input.call(syn::Attribute::parse_outer)?; let vis: syn::Visibility = input.parse()?; let original_name: syn::Ident = input.parse()?; let mut generics: syn::Generics = input.parse()?; let wc: Option = input.parse()?; generics.where_clause = wc; let name = gen_mock_ident(&original_name); let impl_content; let _brace_token = braced!(impl_content in input); let mut consts = Vec::new(); let mut methods = Vec::new(); while !impl_content.is_empty() { let item: ImplItem = impl_content.parse()?; match item { ImplItem::Verbatim(ts) => { let tivf: TraitItemVFn = parse2(ts)?; let mut 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")); } } } let mut 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. if let 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")), } } Ok( MockableStruct { attrs, consts, generics, methods, name, vis, impls } ) } } #[cfg(test)] mod t { use super::*; mod add_lifetime_parameters { use super::*; #[test] fn array() { let mut meth: TraitItemFn = parse2(quote!( fn foo(&self, x: [&dyn T; 1]); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo<'__mockall_x>(&self, x: [&(dyn T + '__mockall_x); 1]);) .to_string(), quote!(#meth).to_string() ); } #[test] fn bare_fn_with_named_args() { let mut meth: TraitItemFn = parse2(quote!( fn foo(&self, x: fn(&dyn T)); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo(&self, x: fn(&dyn T));).to_string(), quote!(#meth).to_string() ); } #[test] fn plain() { let mut meth: TraitItemFn = parse2(quote!( fn foo(&self, x: &dyn T); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x));) .to_string(), quote!(#meth).to_string() ); } #[test] fn slice() { let mut meth: TraitItemFn = parse2(quote!( fn foo(&self, x: &[&dyn T]); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo<'__mockall_x>(&self, x: &[&(dyn T + '__mockall_x)]);) .to_string(), quote!(#meth).to_string() ); } #[test] fn tuple() { let mut meth: TraitItemFn = parse2(quote!( fn foo(&self, x: (&dyn T, u32)); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo<'__mockall_x>(&self, x: (&(dyn T + '__mockall_x), u32));) .to_string(), quote!(#meth).to_string() ); } #[test] fn with_anonymous_lifetime() { let mut meth: TraitItemFn = parse2(quote!( fn foo(&self, x: &(dyn T + '_)); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo(&self, x: &(dyn T + '_));).to_string(), quote!(#meth).to_string() ); } #[test] fn with_parens() { let mut meth: TraitItemFn = parse2(quote!( fn foo(&self, x: &(dyn T)); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x));) .to_string(), quote!(#meth).to_string() ); } #[test] fn with_lifetime_parameter() { let mut meth: TraitItemFn = parse2(quote!( fn foo<'a>(&self, x: &(dyn T + 'a)); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo<'a>(&self, x: &(dyn T + 'a));).to_string(), quote!(#meth).to_string() ); } #[test] fn with_static_lifetime() { let mut meth: TraitItemFn = parse2(quote!( fn foo(&self, x: &(dyn T + 'static)); )).unwrap(); add_lifetime_parameters(&mut meth.sig); assert_eq!( quote!(fn foo(&self, x: &(dyn T + 'static));).to_string(), quote!(#meth).to_string() ); } } mod sanity_check_sig { use super::*; #[test] #[should_panic(expected = "Mockall does not support \"impl trait\" in argument position. Use \"T: SomeTrait\" instead.")] fn impl_trait() { let meth: ImplItemFn = parse2(quote!( fn foo(&self, x: impl SomeTrait) {} )).unwrap(); sanity_check_sig(&meth.sig); } } }