summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorInna Palant <ipalant@google.com>2023-12-13 13:06:52 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-12-13 13:06:52 +0000
commit0a4aa7892e506b3787daca11cfae6e4adac8288b (patch)
tree0b3dcb37be9b85ab4a965fe6c4e4dcc1951ede25
parentfd336aab6ba09c7c46093d98de6005d649598c1a (diff)
parent35933ac7835ae3650e98fde9346e53cd17d39f4d (diff)
downloadmockall_derive-0a4aa7892e506b3787daca11cfae6e4adac8288b.tar.gz
Merge remote-tracking branch 'origin/upstream' am: 691813a94e am: 35933ac783
Original change: undetermined Change-Id: If1083989bb5d3a8ce083f86865fe99aec83213ba Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp37
-rw-r--r--Cargo.toml58
l---------LICENSE1
-rw-r--r--LICENSE-APACHE201
-rw-r--r--LICENSE-MIT25
-rw-r--r--METADATA20
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--OWNERS2
-rw-r--r--README.md14
-rw-r--r--cargo_embargo.json12
-rw-r--r--patches/remove-pretty-assertions-dep.patch12
-rw-r--r--patches/use-deprecated-syn1-dep.patch21
-rw-r--r--patches/use-explicit-extern-crate-proc-macro.patch14
-rw-r--r--src/automock.rs325
-rw-r--r--src/lib.rs1761
-rw-r--r--src/mock_function.rs2436
-rw-r--r--src/mock_item.rs172
-rw-r--r--src/mock_item_struct.rs456
-rw-r--r--src/mock_trait.rs184
-rw-r--r--src/mockable_item.rs164
-rw-r--r--src/mockable_struct.rs642
22 files changed, 6563 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..1232443
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "d5351f7215c6c5bca11f704ed41d9ae768b43007"
+ },
+ "path_in_vcs": "mockall_derive"
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..2220f21
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,37 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file as changes will be overridden on upgrade.
+
+rust_proc_macro {
+ name: "libmockall_derive",
+ crate_name: "mockall_derive",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.11.4",
+ srcs: ["src/lib.rs"],
+ edition: "2018",
+ rustlibs: [
+ "libcfg_if",
+ "libproc_macro2",
+ "libquote",
+ "libsyn_deprecated",
+ ],
+}
+
+rust_test_host {
+ name: "mockall_derive_test_src_lib",
+ crate_name: "mockall_derive",
+ cargo_env_compat: true,
+ cargo_pkg_version: "0.11.4",
+ srcs: ["src/lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ rustlibs: [
+ "libcfg_if",
+ "libproc_macro2",
+ "libquote",
+ "libsyn_deprecated",
+ ],
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..89d8bc1
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,58 @@
+# 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 = "2018"
+name = "mockall_derive"
+version = "0.11.4"
+authors = ["Alan Somers <asomers@gmail.com>"]
+description = """
+Procedural macros for Mockall
+"""
+documentation = "https://docs.rs/mockall_derive"
+readme = "README.md"
+keywords = [
+ "mock",
+ "mocking",
+ "testing",
+]
+categories = ["development-tools::testing"]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/asomers/mockall"
+
+[package.metadata.release]
+push = false
+tag = false
+
+[lib]
+proc-macro = true
+
+[dependencies.cfg-if]
+version = "1.0"
+
+[dependencies.proc-macro2]
+version = "1.0"
+
+[dependencies.quote]
+version = "1.0"
+
+[dependencies.syn]
+version = "1.0.87"
+features = [
+ "extra-traits",
+ "full",
+]
+
+[dev-dependencies.pretty_assertions]
+version = "1.3"
+
+[features]
+nightly_derive = ["proc-macro2/nightly"]
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..6b579aa
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE-APACHE \ No newline at end of file
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ 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.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..39df3da
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (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.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..eaa868e
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "mockall_derive"
+description: "()"
+third_party {
+ identifier {
+ type: "crates.io"
+ value: "https://crates.io/crates/mockall_derive"
+ }
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/mockall_derive/mockall_derive-0.11.4.crate"
+ }
+ version: "0.11.4"
+ # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2023
+ month: 11
+ day: 6
+ }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..48bea6e
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 688011
+include platform/prebuilts/rust:main:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2fa2632
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# 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
+
+
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..f14bd80
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,12 @@
+{
+ "tests": true,
+ "package": {
+ "mockall_derive": {
+ "device_supported": false,
+ "dep_blocklist": [
+ "libpretty_assertions"
+ ],
+ "patch": "patches/use-deprecated-syn1-dep.patch"
+ }
+ }
+}
diff --git a/patches/remove-pretty-assertions-dep.patch b/patches/remove-pretty-assertions-dep.patch
new file mode 100644
index 0000000..8340ef6
--- /dev/null
+++ b/patches/remove-pretty-assertions-dep.patch
@@ -0,0 +1,12 @@
+Not actually needed if the tests pass...
+
+--- a/src/automock.rs 2006-07-24 03:21:28.000000000 +0200
++++ b/src/automock.rs 2023-11-06 14:54:02.079601472 +0100
+@@ -280,7 +280,6 @@
+ #[cfg(test)]
+ mod t {
+ use super::super::*;
+- use pretty_assertions::assert_eq;
+
+ fn check_substitute_type(
+ attrs: TokenStream,
diff --git a/patches/use-deprecated-syn1-dep.patch b/patches/use-deprecated-syn1-dep.patch
new file mode 100644
index 0000000..5d85ecf
--- /dev/null
+++ b/patches/use-deprecated-syn1-dep.patch
@@ -0,0 +1,21 @@
+Patch Android.bp to use the old syn v1 package.
+
+--- a/Android.bp 2023-11-06 16:05:46.887866053 +0100
++++ b/Android.bp 2023-11-06 16:06:15.768058832 +0100
+@@ -14,7 +14,7 @@
+ "libcfg_if",
+ "libproc_macro2",
+ "libquote",
+- "libsyn",
++ "libsyn_deprecated",
+ ],
+ }
+
+@@ -34,6 +34,6 @@
+ "libcfg_if",
+ "libproc_macro2",
+ "libquote",
+- "libsyn",
++ "libsyn_deprecated",
+ ],
+ }
diff --git a/patches/use-explicit-extern-crate-proc-macro.patch b/patches/use-explicit-extern-crate-proc-macro.patch
new file mode 100644
index 0000000..5985b65
--- /dev/null
+++ b/patches/use-explicit-extern-crate-proc-macro.patch
@@ -0,0 +1,14 @@
+Cargo automatically imports proc_macro in Rust 2018, but Soong only
+does this for rust_proc_macro targets.
+
+--- a/src/lib.rs 2023-11-06 16:32:46.480193187 +0100
++++ b/src/lib.rs 2023-11-06 16:29:38.743132168 +0100
+@@ -34,6 +34,8 @@
+ use crate::mock_item_struct::MockItemStruct;
+ use crate::mockable_item::MockableItem;
+
++extern crate proc_macro;
++
+ // Define deterministic aliases for these common types.
+ type HashMap<K, V> = std::collections::HashMap<K, V, BuildHasherDefault<std::collections::hash_map::DefaultHasher>>;
+ type HashSet<K> = std::collections::HashSet<K, BuildHasherDefault<std::collections::hash_map::DefaultHasher>>;
diff --git a/src/automock.rs b/src/automock.rs
new file mode 100644
index 0000000..bba6cb1
--- /dev/null
+++ b/src/automock.rs
@@ -0,0 +1,325 @@
+// 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 {
+ Mod(ItemMod),
+ Type(TraitItemType),
+}
+
+impl Parse for Attr {
+ fn parse(input: ParseStream) -> parse::Result<Self> {
+ let lookahead = input.lookahead1();
+ if lookahead.peek(Token![mod]) {
+ input.parse().map(Attr::Mod)
+ } else 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<Ident, Type>,
+ pub modname: Option<Ident>,
+}
+
+impl Attrs {
+ fn get_path(&self, path: &Path) -> Option<Type> {
+ 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::Method(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::Type(ty) => self.substitute_type(ty, traitname),
+ GenericArgument::Binding(binding) => {
+ self.substitute_type(&mut binding.ty, traitname);
+ }
+ _ => {
+ /*
+ * Nothing to do, as long as lifetimes can't be
+ * associated types
+ */
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// 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::Method(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<Self> {
+ let mut attrs = HashMap::new();
+ let mut modname = None;
+ while !input.is_empty() {
+ let attr: Attr = input.parse()?;
+ match attr {
+ Attr::Mod(item_mod) => {
+ if let Some((br, _)) = item_mod.content {
+ compile_error(
+ br.span,
+ "mod name attributes must have the form \"mod my_name;\"",
+ );
+ }
+ modname = Some(item_mod.ident.clone());
+ }
+ 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, modname })
+ }
+}
+
+/// Unit tests for `Attrs`.
+#[cfg(test)]
+mod t {
+ use super::super::*;
+
+ 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!(<Self as Foo>::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!(<Self as AsRef>::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!(<Self as Foo>::Q),
+ format_ident!("Foo"),
+ quote!(u32),
+ );
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..6308c45
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,1761 @@
+// 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;
+
+extern crate proc_macro;
+
+// Define deterministic aliases for these common types.
+type HashMap<K, V> = std::collections::HashMap<K, V, BuildHasherDefault<std::collections::hash_map::DefaultHasher>>;
+type HashSet<K> = std::collections::HashSet<K, BuildHasherDefault<std::collections::hash_map::DefaultHasher>>;
+
+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!("{}. More information may be available when mockall is built with the \"nightly\" feature.", msg);
+ }
+ }
+}
+
+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<FnArg, Token![,]>) ->
+ (Generics, Vec<FnArg>, Vec<TokenStream>)
+{
+ 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<dyn #tb>)).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::<Ident>(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::<Punctuated<_, _>>();
+ 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::<Ident>(quote!(#bounded_ty)) {
+ !should_remove(&ident)
+ } else {
+ // We can't yet handle where clauses this complicated
+ true
+ }
+ } else {
+ true
+ }
+ }).cloned()
+ .collect::<Punctuated<_, _>>();
+ 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<Fn> 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<dyn trait>" 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<Box<dyn #bounds>>)).unwrap();
+ } else {
+ *ty = parse2(quote!(Box<dyn #bounds>)).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::<Punctuated<WherePredicate, Token![,]>>();
+ wc.predicates = new_predicates;
+ }
+ if generics.where_clause.as_ref()
+ .map(|wc| wc.predicates.is_empty())
+ .unwrap_or(false)
+ {
+ generics.where_clause = None;
+ }
+}
+
+/// Remove any mutability qualifiers from a method's argument list
+fn demutify(inputs: &mut Punctuated<FnArg, token::Comma>) {
+ for arg in inputs.iter_mut() {
+ 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::<Punctuated<_, _>>();
+ 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::Binding(b) =>
+ deselfify(&mut b.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<FnArg, Token![,]>,
+ actual: &Ident,
+ generics: &Generics)
+{
+ for arg in args.iter_mut() {
+ if let FnArg::Typed(pt) = arg {
+ 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<Lifetime> {
+ 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<Lifetime> {
+ 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::Binding(b) => {
+ ret.extend(find_lifetimes(&b.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<Lifetime> {
+ 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,
+}
+
+impl<'a> AttrFormatter<'a> {
+ fn new(attrs: &'a [Attribute]) -> AttrFormatter<'a> {
+ Self {
+ attrs,
+ async_trait: true,
+ doc: true
+ }
+ }
+
+ 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
+ }
+
+ // 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<Attribute> {
+ self.attrs.iter()
+ .cloned()
+ .filter(|attr| {
+ let i = attr.path.get_ident();
+ 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() == "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 {
+ true
+ }
+ }).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::Binding(ref mut binding) => {
+ binding.ty = supersuperfy(&binding.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<TypeParamBound, Token![+]>,
+ 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::<Punctuated::<Ident, Token![,]>>();
+ 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,
+ (Eq(xeq), Eq(yeq)) => xeq.lhs_ty == yeq.lhs_ty,
+ _ => 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 = yt.default.clone();
+ }
+ // 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
+}
+
+/// Transform a Vec of lifetimes into a Generics
+fn lifetimes_to_generics(lv: &Punctuated<LifetimeDef, Token![,]>)-> Generics {
+ if lv.is_empty() {
+ Generics::default()
+ } else {
+ let params = lv.iter()
+ .map(|lt| GenericParam::Lifetime(lt.clone()))
+ .collect();
+ 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: &[FnArg],
+ rt: &ReturnType)
+ -> (Generics,
+ Punctuated<LifetimeDef, token::Comma>,
+ Punctuated<LifetimeDef, token::Comma>)
+{
+ 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::<Lifetime>::default();
+ let mut rlts = HashSet::<Lifetime>::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(&ltd.lifetime) =>
+ rlv.push(ltd),
+ GenericParam::Lifetime(ltd) if alts.contains(&ltd.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" {
+ vr.clone().into()
+ } else {
+ let mut out = vr.clone();
+ out.in_token = Some(in_token);
+ for _ in 0..levels {
+ out.path.segments.insert(0, super_token.into());
+ }
+ out.into()
+ }
+ },
+ _ => 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<M: Into<MockableItem>>(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]
+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 does not contain {:?}", &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 = r#"
+ Foo {
+ fn foo(&self);
+ pub fn bar(&self);
+ pub(crate) fn baz(&self);
+ pub(super) fn bean(&self);
+ pub(in crate::outer) fn boom(&self);
+ }
+ "#;
+ let ts = proc_macro2::TokenStream::from_str(code).unwrap();
+ let output = do_mock(ts).to_string();
+ assert_not_contains(&output, quote!(pub fn foo));
+ assert!(!output.contains(") fn foo"));
+ assert_contains(&output, quote!(pub fn bar));
+ assert_contains(&output, quote!(pub(crate) fn baz));
+ assert_contains(&output, quote!(pub(super) fn bean));
+ assert_contains(&output, quote!(pub(in crate::outer) fn boom));
+
+ 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 specific_impl() {
+ let code = r#"
+ pub Foo<T: 'static> {}
+ impl Bar for Foo<u32> {
+ fn bar(&self);
+ }
+ impl Bar for Foo<i32> {
+ 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<u32>));
+ assert_contains(&output, quote!(impl Bar for MockFoo<i32>));
+ // 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<u32>
+ ));
+ assert_contains(&output, quote!(
+ pub fn expect_bar(&mut self) -> &mut __mock_MockFoo_Bar::__bar::Expectation<i32>
+ ));
+ }
+}
+
+/// 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 = r#"
+ 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 = r#"
+ 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]
+ #[should_panic(expected = "can only mock inline modules")]
+ fn external_module() {
+ let code = r#"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 = r#"
+ 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 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<Output=i32>),
+ quote!(-> ::std::pin::Pin<Box<dyn Future<Output=i32>>>)
+ );
+ }
+
+ // Future is a special case, wherever it appears
+ #[test]
+ fn impl_future_reverse() {
+ check_deimplify(
+ quote!(-> impl Send + Future<Output=i32>),
+ quote!(-> ::std::pin::Pin<Box<dyn Send + Future<Output=i32>>>)
+ );
+ }
+
+ // Stream is a special case
+ #[test]
+ fn impl_stream() {
+ check_deimplify(
+ quote!(-> impl Stream<Item=i32>),
+ quote!(-> ::std::pin::Pin<Box<dyn Stream<Item=i32>>>)
+ );
+ }
+
+ #[test]
+ fn impl_trait() {
+ check_deimplify(
+ quote!(-> impl Foo),
+ quote!(-> Box<dyn Foo>)
+ );
+ }
+
+ // With extra bounds
+ #[test]
+ fn impl_trait2() {
+ check_deimplify(
+ quote!(-> impl Foo + Send),
+ quote!(-> Box<dyn Foo + Send>)
+ );
+ }
+}
+
+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 future() {
+ check_deselfify(
+ quote!(Box<dyn Future<Output=Self>>),
+ quote!(Foo),
+ quote!(),
+ quote!(Box<dyn Future<Output=Foo>>)
+ );
+ }
+
+ #[test]
+ fn qself() {
+ check_deselfify(
+ quote!(<Self as Self>::Self),
+ quote!(Foo),
+ quote!(),
+ quote!(<Foo as Foo>::Foo)
+ );
+ }
+
+ #[test]
+ fn trait_object() {
+ check_deselfify(
+ quote!(Box<dyn Self>),
+ quote!(Foo),
+ quote!(),
+ quote!(Box<dyn Foo>)
+ );
+ }
+
+ // A trait object with multiple bounds
+ #[test]
+ fn trait_object2() {
+ check_deselfify(
+ quote!(Box<dyn Self + Send>),
+ quote!(Foo),
+ quote!(),
+ quote!(Box<dyn Foo + Send>)
+ );
+ }
+}
+
+mod dewhereselfify {
+ use super::*;
+
+ #[test]
+ fn lifetime() {
+ let mut meth: ImplItemMethod = parse2(quote!(
+ fn foo<'a>(&self) where 'a: 'static, Self: Sized;
+ )).unwrap();
+ let expected: ImplItemMethod = parse2(quote!(
+ fn foo<'a>(&self) where 'a: 'static;
+ )).unwrap();
+ dewhereselfify(&mut meth.sig.generics);
+ assert_eq!(meth, expected);
+ }
+
+ #[test]
+ fn normal_method() {
+ let mut meth: ImplItemMethod = parse2(quote!(
+ fn foo(&self) where Self: Sized;
+ )).unwrap();
+ let expected: ImplItemMethod = parse2(quote!(
+ fn foo(&self);
+ )).unwrap();
+ dewhereselfify(&mut meth.sig.generics);
+ assert_eq!(meth, expected);
+ }
+
+ #[test]
+ fn with_real_generics() {
+ let mut meth: ImplItemMethod = parse2(quote!(
+ fn foo<T>(&self, t: T) where Self: Sized, T: Copy;
+ )).unwrap();
+ let expected: ImplItemMethod = parse2(quote!(
+ fn foo<T>(&self, t: T) where T: Copy;
+ )).unwrap();
+ dewhereselfify(&mut meth.sig.generics);
+ assert_eq!(meth, expected);
+ }
+}
+
+mod gen_keyid {
+ use super::*;
+
+ 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!(<T>), quote!(<T>));
+ }
+
+ #[test]
+ fn twotypes() {
+ check_gen_keyid(quote!(<T, V>), quote!(<(T, V)>));
+ }
+}
+
+mod merge_generics {
+ use super::*;
+
+ #[test]
+ fn both() {
+ let mut g1: Generics = parse2(quote!(<T: 'static, V: Copy> )).unwrap();
+ let wc1: WhereClause = parse2(quote!(where T: Default)).unwrap();
+ g1.where_clause = Some(wc1);
+
+ let mut g2: Generics = parse2(quote!(<Q: Send, V: Clone>)).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!(
+ <T: 'static, V: Copy + Clone, Q: Send>
+ )).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!(<T: 'static, V: Copy> )).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!(<T: 'static, V: Copy> )).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!(<Q: Send, V: Clone>)).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<u32>),
+ quote!(::super::super::SuperT<u32>)
+ );
+ }
+
+ #[test]
+ fn path_with_qself() {
+ check_supersuperfy(
+ quote!(<super::X as super::Y>::Foo<u32>),
+ quote!(<super::super::X as super::super::Y>::Foo<u32>),
+ );
+ }
+
+ #[test]
+ fn angle_bracketed_generic_arguments() {
+ check_supersuperfy(
+ quote!(mod_::T<super::X>),
+ quote!(mod_::T<super::super::X>)
+ );
+ }
+
+ #[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!(<T: X = super::Y>), quote!(),
+ quote!(<T: X = super::super::Y>), quote!(),
+ );
+ }
+
+ #[test]
+ fn empty() {
+ check_supersuperfy_generics(quote!(), quote!(), quote!(), quote!());
+ }
+
+ #[test]
+ fn everything() {
+ check_supersuperfy_generics(
+ quote!(<T: super::A = super::B>),
+ quote!(where super::C: super::D),
+ quote!(<T: super::super::A = super::super::B>),
+ quote!(where super::super::C: super::super::D),
+ );
+ }
+
+ #[test]
+ fn bound() {
+ check_supersuperfy_generics(
+ quote!(<T: super::A>), quote!(),
+ quote!(<T: super::super::A>), quote!(),
+ );
+ }
+
+ #[test]
+ fn closure() {
+ check_supersuperfy_generics(
+ quote!(<F: Fn(u32) -> super::SuperT>), quote!(),
+ quote!(<F: Fn(u32) -> 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),
+ );
+ }
+}
+}
diff --git a/src/mock_function.rs b/src/mock_function.rs
new file mode 100644
index 0000000..86a8690
--- /dev/null
+++ b/src/mock_function.rs
@@ -0,0 +1,2436 @@
+// vim: tw=80
+use super::*;
+
+use quote::ToTokens;
+
+/// 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<WhereClause>, 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: <Token![where]>::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<usize>,
+ 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;
+ 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) =
+ declosurefy(&self.sig.generics, &self.sig.inputs);
+
+ for fa in declosured_inputs.iter() {
+ if let FnArg::Typed(pt) = fa {
+ let argname = (*pt.pat).clone();
+ if pat_is_self(&argname) {
+ // A weird receiver like `Box<Self>`
+ is_static = false;
+ continue;
+ }
+ 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(<Token![->]>::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(<Token![->]>::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::<HashSet<LifetimeDef>>()
+ .union(&malifetimes.into_iter().collect::<HashSet<_>>())
+ .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),
+ 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: self.sig.clone(),
+ 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: &[],
+ 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<LifetimeDef, token::Comma>,
+ /// Names of the method arguments
+ argnames: Vec<Pat>,
+ /// Types of the method arguments
+ argty: Vec<Type>,
+ /// any attributes on the original function, like #[inline]
+ pub attrs: Vec<Attribute>,
+ /// Expressions that should be used for Expectation::call's arguments
+ call_exprs: Vec<TokenStream>,
+ /// Generics used for the expectation call
+ call_generics: Generics,
+ /// Visibility of the mock function itself
+ call_vis: Visibility,
+ /// 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<Ident>,
+ /// 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<TokenStream>,
+ /// Types used for Predicates. Will be almost the same as args, but every
+ /// type will be a non-reference type.
+ predty: Vec<Type>,
+ /// 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<Type>,
+ /// The signature of the mockable function
+ sig: Signature,
+ /// Name of the parent structure, if any
+ struct_: Option<Ident>,
+ /// Generics of the parent structure
+ struct_generics: Generics,
+ /// Name of this method's trait, if the method comes from a trait
+ trait_: Option<Ident>,
+ /// 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).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)
+ };
+ let substruct_obj = 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
+ #vis #sig {
+ let no_match_msg = #no_match_msg;
+ #deref {
+ let __mockall_guard = #outer_mod_path::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
+ #vis #sig {
+ 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::EXPECTATIONS.lock()
+ .unwrap()
+ .checkpoint()
+ .collect::<Vec<_>>();
+ }
+ )
+ } 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::MaybeDebugger(&#argnames)),*))
+ }
+
+ /// 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<T>`.
+ // 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!("{}_expectations", trait_);
+ 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<BoundLifetimes> {
+ if self.alifetimes.is_empty() {
+ None
+ } else {
+ Some(BoundLifetimes {
+ lifetimes: self.alifetimes.clone(),
+ lt_token: <Token![<]>::default(),
+ gt_token: <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<dyn ToTokens> = 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<dyn ToTokens> = 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<dyn ToTokens> = 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<dyn ToTokens> = 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)]
+ 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::<Vec<_>>();
+ let with_generics = with_generics_idents
+ .iter()
+ .zip(self.f.predty.iter())
+ .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ))
+ .collect::<TokenStream>();
+ let with_args = self
+ .f
+ .argnames
+ .iter()
+ .zip(with_generics_idents.iter())
+ .map(|(argname, id)| quote!(#argname: #id, ))
+ .collect::<TokenStream>();
+ let boxed_withargs = argnames
+ .iter()
+ .map(|aa| quote!(Box::new(#aa), ))
+ .collect::<TokenStream>();
+ quote!(
+ /// Holds the stuff that is independent of the output type
+ struct Common #ig #wc {
+ matcher: Mutex<Matcher #tg>,
+ 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<MockallR>(&mut self, __mockall_r: MockallR)
+ where MockallR: Into<::mockall::TimesRange>
+ {
+ self.times.times(__mockall_r)
+ }
+
+ 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)));
+ }
+
+ fn withf<MockallF>(&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<MockallF>(&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::<Vec<_>>();
+ let with_generics = with_generics_idents
+ .iter()
+ .zip(self.f.predty.iter())
+ .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ))
+ .collect::<TokenStream>();
+ let with_args = self
+ .f
+ .argnames
+ .iter()
+ .zip(with_generics_idents.iter())
+ .map(|(argname, id)| quote!(#argname: #id, ))
+ .collect::<TokenStream>();
+ let v = &self.f.privmod_vis;
+ 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<MockallR>(&mut self, __mockall_r: MockallR) -> &mut Self
+ where MockallR: Into<::mockall::TimesRange>
+ {
+ self.common.times(__mockall_r);
+ self
+ }
+
+ /// Set matching crieteria 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
+ }
+
+ /// 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<MockallF>(&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<MockallF>(&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<Expectation #tg>) #wc;
+
+ impl #ig Expectations #tg #wc {
+ /// Verify that all current expectations are satisfied and clear
+ /// them.
+ #v fn checkpoint(&mut self) -> std::vec::Drain<Expectation #tg>
+ {
+ 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 fn new() -> Self {
+ Self::default()
+ }
+ }
+ impl #ig Default for Expectations #tg #wc
+ {
+ fn default() -> Self {
+ Expectations(Vec::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::<Expectations #tg>()
+ .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::<Vec<_>>();
+ let with_generics = with_generics_idents
+ .iter()
+ .zip(self.f.predty.iter())
+ .map(|(id, mt)| quote!(#id: #hrtb ::mockall::Predicate<#mt> + Send + 'static, ))
+ .collect::<TokenStream>();
+ let with_args = self
+ .f
+ .argnames
+ .iter()
+ .zip(with_generics_idents.iter())
+ .map(|(argname, id)| quote!(#argname: #id, ))
+ .collect::<TokenStream>();
+ let v = &self.f.privmod_vis;
+ 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<MockallOutput>
+ (&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<MockallOutput>
+ (&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<MockallF>(&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<MockallF>(&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<MockallF>(&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<MockallF>(&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<MockallR>(&mut self, __mockall_r: MockallR)
+ -> &mut Expectation #tg
+ where MockallR: Into<::mockall::TimesRange>
+ {
+ #expectations.0[self.i].times(__mockall_r)
+ }
+
+ /// 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, )*)
+ }
+
+ /// Just like
+ /// [`Expectation::withf`](struct.Expectation.html#method.withf)
+ #v fn withf<MockallF>(&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<MockallF>(&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 = LifetimeDef::new(Lifetime::new("'__mockall_lt", Span::call_site()));
+ let mut e_generics = self.f.egenerics.clone();
+ e_generics.lt_token.get_or_insert(<Token![<]>::default());
+ e_generics.params.push(GenericParam::Lifetime(ltdef));
+ e_generics.gt_token.get_or_insert(<Token![>]>::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!(
+ ::mockall::lazy_static! {
+ #[doc(hidden)]
+ #v static ref EXPECTATIONS:
+ ::std::sync::Mutex<Expectations #tg> =
+ ::std::sync::Mutex::new(Expectations::new());
+ }
+ /// 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 = LifetimeDef::new(Lifetime::new("'__mockall_lt", Span::call_site()));
+ let mut egenerics = self.f.egenerics.clone();
+ egenerics.lt_token.get_or_insert(<Token![<]>::default());
+ egenerics.params.push(GenericParam::Lifetime(ltdef));
+ egenerics.gt_token.get_or_insert(<Token![>]>::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!(
+ ::mockall::lazy_static! {
+ #v static ref EXPECTATIONS:
+ ::std::sync::Mutex<GenericExpectations> =
+ ::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 = LifetimeDef::new(Lifetime::new("'__mockall_lt", Span::call_site()));
+ let mut egenerics = self.f.egenerics.clone();
+ egenerics.lt_token.get_or_insert(<Token![<]>::default());
+ egenerics.params.push(GenericParam::Lifetime(ltdef));
+ egenerics.gt_token.get_or_insert(<Token![>]>::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 = LifetimeDef::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::<Punctuated<Ident, Token![,]>>();
+ 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!();
+
+ 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<Fn(generics)> is Send even if
+ // generics are not, unlike PhantomData<generics>
+ _phantom: ::std::marker::PhantomData<
+ Box<dyn Fn(#ctx_fn_params) + Send>
+ >
+ }
+ 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 = EXPECTATIONS
+ .lock()
+ .unwrap()
+ .checkpoint()
+ .collect::<Vec<_>>();
+ }
+
+ /// Create a new expectation for this method.
+ #must_use
+ #v fn expect #meth_ig ( &self,) -> ExpectationGuard #e_tg
+ #meth_wc
+ {
+ ExpectationGuard::new(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) {
+ 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::<Vec<_>>();
+ 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::<TokenStream>();
+ let preds = self
+ .f
+ .predty
+ .iter()
+ .map(|t| quote!(Box<dyn #hrtb ::mockall::Predicate<#t> + Send>,))
+ .collect::<TokenStream>();
+ let predty = &self.f.predty;
+ let refpredty = &self.f.refpredty;
+ quote!(
+ enum Matcher #ig #wc {
+ Always,
+ Func(Box<dyn #hrtb Fn(#( #refpredty, )*) -> bool + Send>),
+ // Version of Matcher::Func for closures that aren't Send
+ FuncSt(::mockall::Fragile<Box<dyn #hrtb Fn(#( #refpredty, )*) -> bool>>),
+ Pred(Box<(#preds)>),
+ // Prevent "unused type parameter" errors
+ // Surprisingly, PhantomData<Fn(generics)> is Send even if
+ // generics are not, unlike PhantomData<generics>
+ _Phantom(Box<dyn Fn(#(#fn_params,)*) + Send>)
+ }
+ 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, )*),
+ Matcher::Pred(__mockall_pred) =>
+ [#pred_matches]
+ .iter()
+ .all(|__mockall_x| *__mockall_x),
+ _ => 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, "<anything>"),
+ Matcher::Func(_) => write!(__mockall_fmt, "<function>"),
+ Matcher::FuncSt(_) => write!(__mockall_fmt, "<single threaded function>"),
+ Matcher::Pred(__mockall_p) => {
+ write!(__mockall_fmt, #braces,
+ #(__mockall_p.#indices,)*)
+ }
+ _ => 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<Fn(generics)> is Send even if generics are not,
+ // unlike PhantomData<generics>
+ _Phantom(Mutex<Box<dyn Fn(#(#fn_params,)*) + Send>>)
+ }
+
+ 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<dyn FnMut(#(#argty, )*) -> #owned_output + Send + Sync>),
+ Option<#owned_output>),
+ // Version of Rfunc::Mut for closures that aren't Send
+ MutSt((::mockall::Fragile<
+ Box<dyn FnMut(#(#argty, )*) -> #owned_output >>
+ ), Option<#owned_output>
+ ),
+ Var(#owned_output),
+ // Prevent "unused type parameter" errors Surprisingly,
+ // PhantomData<Fn(generics)> is Send even if generics are not,
+ // unlike PhantomData<generics>
+ _Phantom(Mutex<Box<dyn Fn(#(#fn_params,)*) + Send>>)
+ }
+
+ 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<dyn #hrtb FnMut(#(#argty, )*) -> #output + Send>),
+ // Version of Rfunc::Mut for closures that aren't Send
+ MutSt(::mockall::Fragile<
+ Box<dyn #hrtb FnMut(#(#argty, )*) -> #output >>
+ ),
+ Once(Box<dyn #hrtb FnOnce(#(#argty, )*) -> #output + Send>),
+ // Version of Rfunc::Once for closure that aren't Send
+ OnceSt(::mockall::Fragile<
+ Box<dyn #hrtb FnOnce(#(#argty, )*) -> #output>>
+ ),
+ // Prevent "unused type parameter" errors Surprisingly,
+ // PhantomData<Fn(generics)> is Send even if generics are not,
+ // unlike PhantomData<generics>
+ _Phantom(Box<dyn Fn(#(#fn_params,)*) + Send>)
+ }
+
+ 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
+ {
+ 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
+ {
+ 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<MockallF>(&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<MockallF>(&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<Rfunc #tg>,
+ }
+
+ #[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
+ {
+ 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<MockallOutput>(&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<MockallOutput>(&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<MockallF>(&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<MockallF>(&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<MockallF>(&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<MockallF>(&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<dyn ::mockall::AnyExpectations>>
+ }
+ 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<dyn ::mockall::AnyExpectations>>
+ {
+ 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::<Expectations #tg>()
+ .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::<Expectations #tg>()
+ .unwrap()
+ .expect()
+ }
+ }
+ )
+ .to_tokens(tokens)
+ }
+}
diff --git a/src/mock_item.rs b/src/mock_item.rs
new file mode 100644
index 0000000..109d9c2
--- /dev/null
+++ b/src/mock_item.rs
@@ -0,0 +1,172 @@
+// vim: tw=80
+use super::*;
+
+use crate::{
+ mock_function::MockFunction,
+ mockable_item::{MockableItem, MockableModule},
+};
+
+/// A Mock item
+pub(crate) enum MockItem {
+ Module(MockItemModule),
+ Struct(MockItemStruct),
+}
+
+impl From<MockableItem> 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<MockFunction>),
+ Tokens(TokenStream),
+}
+
+pub(crate) struct MockItemModule {
+ attrs: TokenStream,
+ vis: Visibility,
+ mock_ident: Ident,
+ orig_ident: Option<Ident>,
+ content: Vec<MockItemContent>,
+}
+
+impl From<MockableModule> 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()));
+ 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 `{}` module", ident);
+ 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);
+ }
+}
diff --git a/src/mock_item_struct.rs b/src/mock_item_struct.rs
new file mode 100644
index 0000000..9bf5c13
--- /dev/null
+++ b/src/mock_item_struct.rs
@@ -0,0 +1,456 @@
+// vim: tw=80
+use super::*;
+
+use quote::ToTokens;
+use std::collections::HashSet;
+
+use crate::{mock_function::MockFunction, mock_trait::MockTrait};
+
+fn phantom_default_inits(generics: &Generics) -> Vec<TokenStream> {
+ 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<TokenStream> {
+ 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<Item = &'a MockTrait>>(
+ i: I,
+) -> impl Iterator<Item = &'a MockTrait> {
+ let mut hs = HashSet::<(Path, Vec<Attribute>)>::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<MockFunction>);
+
+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<impl ToTokens> {
+ self.0
+ .iter()
+ .filter(|meth| !meth.is_static())
+ .map(|meth| meth.checkpoint())
+ .collect::<Vec<_>>()
+ }
+
+ /// Return a fragment of code to initialize struct fields during default()
+ fn default_inits(&self) -> Vec<TokenStream> {
+ 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::<Vec<_>>()
+ }
+
+ fn field_definitions(&self, modname: &Ident) -> Vec<TokenStream> {
+ self.0
+ .iter()
+ .filter(|meth| !meth.is_static())
+ .map(|meth| meth.field_definition(Some(modname)))
+ .collect::<Vec<_>>()
+ }
+
+ fn priv_mods(&self) -> Vec<impl ToTokens> {
+ self.0
+ .iter()
+ .map(|meth| meth.priv_module())
+ .collect::<Vec<_>>()
+ }
+}
+
+pub(crate) struct MockItemStruct {
+ attrs: Vec<Attribute>,
+ consts: Vec<ImplItemConst>,
+ 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<MockTrait>,
+ 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 `<MockX as TraitY>::new`
+ pub fn new() -> Self {
+ Self::default()
+ }
+ )
+ }
+ }
+
+ fn phantom_default_inits(&self) -> Vec<TokenStream> {
+ phantom_default_inits(&self.generics)
+ }
+
+ fn phantom_fields(&self) -> Vec<TokenStream> {
+ phantom_fields(&self.generics)
+ }
+}
+
+impl From<MockableStruct> 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::Method(iim) = ii {
+ iim.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::<Vec<_>>(),
+ );
+ 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).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::<Vec<_>>();
+ let contexts = self
+ .methods
+ .0
+ .iter()
+ .filter(|meth| meth.is_static())
+ .map(|meth| meth.context_fn(Some(modname)))
+ .collect::<Vec<_>>();
+ let expects = self
+ .methods
+ .0
+ .iter()
+ .filter(|meth| !meth.is_static())
+ .map(|meth| meth.expect(modname, None))
+ .collect::<Vec<_>>();
+ 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::<Vec<_>>();
+ 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::<Vec<_>>();
+ 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::<Vec<_>>();
+ 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::<Vec<_>>();
+ 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::<Vec<_>>();
+ 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<Attribute>,
+ 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<TokenStream> {
+ phantom_default_inits(&self.generics)
+ }
+
+ fn phantom_fields(&self) -> Vec<TokenStream> {
+ phantom_fields(&self.generics)
+ }
+}
+
+impl ToTokens for MockItemTraitImpl {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let 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)]
+ #(#attrs)*
+ pub mod #modname {
+ use super::*;
+ #(#priv_mods)*
+ }
+ #[allow(non_camel_case_types)]
+ #[allow(non_snake_case)]
+ #[allow(missing_docs)]
+ #(#attrs)*
+ struct #struct_name #ig #wc
+ {
+ #(#field_definitions),*
+ }
+ #(#attrs)*
+ impl #ig ::std::default::Default for #struct_name #tg #wc {
+ fn default() -> Self {
+ Self {
+ #(#default_inits),*
+ }
+ }
+ }
+ #(#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);
+ }
+}
diff --git a/src/mock_trait.rs b/src/mock_trait.rs
new file mode 100644
index 0000000..eba29b6
--- /dev/null
+++ b/src/mock_trait.rs
@@ -0,0 +1,184 @@
+// vim: tw=80
+use proc_macro2::Span;
+use quote::{format_ident, quote, ToTokens};
+use std::{
+ collections::hash_map::DefaultHasher,
+ hash::{Hash, Hasher},
+};
+use syn::{spanned::Spanned, *};
+
+use crate::{
+ compile_error,
+ mock_function::{self, MockFunction},
+ AttrFormatter,
+};
+
+pub(crate) struct MockTrait {
+ pub attrs: Vec<Attribute>,
+ pub consts: Vec<ImplItemConst>,
+ pub generics: Generics,
+ pub methods: Vec<MockFunction>,
+ /// 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<ImplItemType>,
+ pub unsafety: Option<Token![unsafe]>,
+}
+
+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::Method(iim) => {
+ let mf = mock_function::Builder::new(&iim.sig, vis)
+ .attrs(&iim.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 = &self.attrs;
+ 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::<Vec<_>>();
+ let contexts = self
+ .methods
+ .iter()
+ .filter(|meth| meth.is_static())
+ .map(|meth| meth.context_fn(Some(modname)))
+ .collect::<Vec<_>>();
+ 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::<Vec<_>>();
+ 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)*
+ }
+ )
+ }
+}
diff --git a/src/mockable_item.rs b/src/mockable_item.rs
new file mode 100644
index 0000000..03c894d
--- /dev/null
+++ b/src/mockable_item.rs
@@ -0,0 +1,164 @@
+// 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::ForeignMod(item_foreign_mod) => {
+ MockableItem::Module(MockableModule::from((attrs, item_foreign_mod)))
+ }
+ 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<MockableStruct> 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<Ident>,
+ pub content: Vec<Item>,
+}
+
+impl From<(Attrs, ItemForeignMod)> for MockableModule {
+ fn from((attrs, foreign): (Attrs, ItemForeignMod)) -> MockableModule {
+ let orig_ident = None;
+ let mock_ident = attrs.modname.expect(concat!(
+ "module name is required when mocking foreign functions,",
+ " like `#[automock(mod mock_ffi)]`"
+ ));
+ let vis = Visibility::Public(VisPublic {
+ pub_token: <Token![pub]>::default(),
+ });
+ let attrs = quote!(
+ #[deprecated(since = "0.9.0", note = "Using automock directly on an extern block is deprecated. Instead, wrap the extern block in a module, and automock that, like #[automock] mod ffi { extern \"C\" { fn foo ... } }")]
+ );
+ let mut content = vec![
+ // When mocking extern blocks, we pretend that they're modules, so
+ // we need a "use super::*;" to ensure that types can resolve
+ Item::Use(ItemUse {
+ attrs: Vec::new(),
+ vis: Visibility::Inherited,
+ use_token: token::Use::default(),
+ leading_colon: None,
+ tree: UseTree::Path(UsePath {
+ ident: Ident::new("super", Span::call_site()),
+ colon2_token: token::Colon2::default(),
+ tree: Box::new(UseTree::Glob(UseGlob {
+ star_token: token::Star::default(),
+ })),
+ }),
+ semi_token: token::Semi::default(),
+ }),
+ ];
+ content.extend(foreign.items.into_iter().map(|foreign_item| {
+ match foreign_item {
+ ForeignItem::Fn(f) => {
+ let span = f.sig.span();
+ let mut sig = f.sig;
+
+ // When mocking extern blocks, we pretend that they're
+ // modules. So we must supersuperfy everything by one
+ // level.
+ let vis = expectation_visibility(&f.vis, 1);
+
+ for arg in sig.inputs.iter_mut() {
+ if let FnArg::Typed(pt) = arg {
+ *pt.ty = supersuperfy(pt.ty.as_ref(), 1);
+ }
+ }
+ if let ReturnType::Type(_, ty) = &mut sig.output {
+ **ty = supersuperfy(&*ty, 1);
+ }
+
+ // Foreign functions are always unsafe. Mock foreign
+ // functions should be unsafe too, to prevent "warning:
+ // unused unsafe" messages.
+ sig.unsafety = Some(Token![unsafe](span));
+ let block = Box::new(Block {
+ brace_token: token::Brace::default(),
+ stmts: Vec::new(),
+ });
+
+ Item::Fn(ItemFn {
+ attrs: f.attrs,
+ vis,
+ sig,
+ block,
+ })
+ }
+ _ => {
+ compile_error(foreign_item.span(), "Unsupported foreign item type");
+ Item::Verbatim(TokenStream::default())
+ }
+ }
+ }));
+ MockableModule {
+ attrs,
+ vis,
+ mock_ident,
+ orig_ident,
+ content,
+ }
+ }
+}
+
+impl From<ItemMod> 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,
+ }
+ }
+}
diff --git a/src/mockable_struct.rs b/src/mockable_struct.rs
new file mode 100644
index 0000000..fe28199
--- /dev/null
+++ b/src/mockable_struct.rs
@@ -0,0 +1,642 @@
+// vim: tw=80
+use super::*;
+use syn::parse::{Parse, ParseStream};
+
+/// Make any implicit lifetime parameters explicit
+fn add_lifetime_parameters(sig: &mut Signature) {
+ fn add_to_trait_object(generics: &mut Generics, var: &Pat, to: &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(LifetimeDef::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 {
+ Attribute {
+ pound_token: <Token![#]>::default(),
+ style: AttrStyle::Outer,
+ bracket_token: token::Bracket::default(),
+ path: Path::from(format_ident!("derive")),
+ tokens: quote!((Debug)),
+ }
+}
+
+/// 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::Method(ref mut iim) = item {
+ mockable_method(iim, name, generics);
+ }
+ }
+ impl_
+}
+
+/// Performs transformations on the method to make it mockable
+fn mockable_method(meth: &mut ImplItemMethod, 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 TraitItemMethod, 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::Method(mut tim) => {
+ mockable_trait_method(&mut tim, name, generics);
+ ImplItem::Method(tim2iim(tim, &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::<Vec<_>>();
+ 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::<AngleBracketedGenericArguments>(quote!(#stg)) {
+ struct_path.segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(abga);
+ }
+ if let Ok(abga) = parse2::<AngleBracketedGenericArguments>(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: <Token![impl]>::default(),
+ generics: generics.clone(),
+ trait_: Some((None, trait_path, <Token![for]>::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",
+ );
+ (<Token![=]>::default(), Expr::Verbatim(TokenStream::new()))
+ });
+ ImplItemConst {
+ attrs: tic.attrs,
+ vis: vis.clone(),
+ defaultness: None,
+ const_token: tic.const_token,
+ ident: tic.ident,
+ colon_token: tic.colon_token,
+ ty: tic.ty,
+ eq_token,
+ expr,
+ semi_token: tic.semi_token,
+ }
+}
+
+/// Converts a TraitItemMethod into an ImplItemMethod
+fn tim2iim(m: syn::TraitItemMethod, vis: &syn::Visibility) -> syn::ImplItemMethod {
+ let empty_block = Block {
+ brace_token: token::Brace::default(),
+ stmts: Vec::new(),
+ };
+ syn::ImplItemMethod {
+ 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,
+ }
+}
+
+pub(crate) struct MockableStruct {
+ pub attrs: Vec<Attribute>,
+ pub consts: Vec<ImplItemConst>,
+ pub generics: Generics,
+ /// Inherent methods of the mockable struct
+ pub methods: Vec<ImplItemMethod>,
+ pub name: Ident,
+ pub vis: Visibility,
+ pub impls: Vec<ItemImpl>,
+}
+
+impl MockableStruct {
+ /// Does this struct derive Debug?
+ pub fn derives_debug(&self) -> bool {
+ self.attrs.iter().any(|attr| {
+ if let Ok(Meta::List(ml)) = attr.parse_meta() {
+ let i = ml.path.get_ident();
+ if i.map_or(false, |i| *i == "derive") {
+ ml.nested.iter().any(|nm| {
+ if let NestedMeta::Meta(m) = nm {
+ let i = m.path().get_ident();
+ i.map_or(false, |i| *i == "Debug")
+ } else {
+ false
+ }
+ })
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ })
+ }
+}
+
+impl From<(Attrs, ItemTrait)> for MockableStruct {
+ fn from((attrs, item_trait): (Attrs, ItemTrait)) -> MockableStruct {
+ let trait_ = attrs.substitute_trait(&item_trait);
+ let mut attrs = trait_.attrs.clone();
+ attrs.push(derive_debug());
+ let vis = trait_.vis.clone();
+ let name = gen_mock_ident(&trait_.ident);
+ let generics = trait_.generics.clone();
+ let impls = vec![mockable_trait(trait_, &name, &generics)];
+ MockableStruct {
+ attrs,
+ consts: Vec::new(),
+ vis,
+ name,
+ generics,
+ methods: Vec::new(),
+ impls,
+ }
+ }
+}
+
+impl From<ItemImpl> for MockableStruct {
+ fn from(mut item_impl: ItemImpl) -> MockableStruct {
+ let name = match &*item_impl.self_ty {
+ Type::Path(type_path) => {
+ let n = find_ident_from_path(&type_path.path).0;
+ gen_mock_ident(&n)
+ }
+ x => {
+ compile_error(
+ x.span(),
+ "mockall_derive only supports mocking traits and structs",
+ );
+ Ident::new("", Span::call_site())
+ }
+ };
+ 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 pub_token = Token![pub](Span::call_site());
+ let vis = Visibility::Public(VisPublic { pub_token });
+ 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. `<Self as MyTrait>::MyType`
+ let mut attrs = Attrs::default();
+ for item in item_impl.items.iter() {
+ match item {
+ ImplItem::Const(_iic) => (),
+ ImplItem::Method(_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::Method(mut meth) => {
+ mockable_method(&mut meth, &name, &item_impl.generics);
+ methods.push(meth)
+ }
+ ImplItem::Const(iic) => consts.push(iic),
+ // Rust doesn't allow types in an inherent impl
+ x => compile_error(x.span(), "Unsupported by Mockall in this context"),
+ }
+ }
+ };
+ MockableStruct {
+ attrs,
+ consts,
+ generics,
+ methods,
+ name,
+ vis,
+ impls,
+ }
+ }
+}
+
+impl Parse for MockableStruct {
+ fn parse(input: ParseStream) -> syn::parse::Result<Self> {
+ let attrs = input.call(syn::Attribute::parse_outer)?;
+ let vis: syn::Visibility = input.parse()?;
+ let original_name: syn::Ident = input.parse()?;
+ let mut generics: syn::Generics = input.parse()?;
+ let wc: Option<syn::WhereClause> = input.parse()?;
+ generics.where_clause = wc;
+ let name = gen_mock_ident(&original_name);
+ let impl_content;
+ let _brace_token = braced!(impl_content in input);
+ let mut consts = Vec::new();
+ let mut methods = Vec::new();
+ while !impl_content.is_empty() {
+ let item: ImplItem = impl_content.parse()?;
+ match item {
+ ImplItem::Method(mut iim) => {
+ 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::Trait(it) => {
+ let note = "Deprecated mock! syntax. Instead of \"trait X\", write \"impl X for Y\". See PR #205";
+ let mut impl_ = mockable_trait(it, &name, &generics);
+ impl_.attrs.push(Attribute {
+ pound_token: <token::Pound>::default(),
+ style: AttrStyle::Outer,
+ bracket_token: token::Bracket::default(),
+ path: Path::from(format_ident!("deprecated")),
+ tokens: quote!((since = "0.9.0", note = #note)),
+ });
+ impls.push(impl_)
+ }
+ Item::Impl(ii) => 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: TraitItemMethod = 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: TraitItemMethod = 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: TraitItemMethod = 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: TraitItemMethod = 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: TraitItemMethod = 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: TraitItemMethod = 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: TraitItemMethod = 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: TraitItemMethod = 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: TraitItemMethod = 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: ImplItemMethod = parse2(quote!(
+ fn foo(&self, x: impl SomeTrait);
+ ))
+ .unwrap();
+ sanity_check_sig(&meth.sig);
+ }
+ }
+}