1// vim: tw=80
2use super::*;
3use std::collections::HashMap;
4use syn::parse::{Parse, ParseStream};
5
6/// A single automock attribute
7// This enum is very short-lived, so it's fine not to box it.
8#[allow(clippy::large_enum_variant)]
9enum Attr {
10 Mod(ItemMod),
11 Type(TraitItemType),
12}
13
14impl Parse for Attr {
15 fn parse(input: ParseStream) -> parse::Result<Self> {
16 let lookahead = input.lookahead1();
17 if lookahead.peek(Token![mod]) {
18 input.parse().map(Attr::Mod)
19 } else if lookahead.peek(Token![type]) {
20 input.parse().map(Attr::Type)
21 } else {
22 Err(lookahead.error())
23 }
24 }
25}
26
27/// automock attributes
28#[derive(Debug, Default)]
29pub(crate) struct Attrs {
30 pub attrs: HashMap<Ident, Type>,
31 pub modname: Option<Ident>
32}
33
34impl Attrs {
35 fn get_path(&self, path: &Path) -> Option<Type> {
36 if path.leading_colon.is_none() & (path.segments.len() == 2) {
37 if path.segments.first().unwrap().ident == "Self" {
38 let ident = &path.segments.last().unwrap().ident;
39 self.attrs.get(ident).cloned()
40 } else {
41 None
42 }
43 } else {
44 None
45 }
46 }
47
48 pub(crate) fn substitute_item_impl(&self, item_impl: &mut ItemImpl) {
49 let (_, trait_path, _) = item_impl.trait_.as_ref()
50 .expect("Should only be called for trait item impls");
51 let trait_ident = find_ident_from_path(trait_path).0;
52 for item in item_impl.items.iter_mut() {
53 if let ImplItem::Method(method) = item {
54 let sig = &mut method.sig;
55 for fn_arg in sig.inputs.iter_mut() {
56 if let FnArg::Typed(arg) = fn_arg {
57 self.substitute_type(&mut arg.ty, &trait_ident);
58 }
59 }
60 if let ReturnType::Type(_, ref mut ty) = &mut sig.output {
61 self.substitute_type(ty, &trait_ident);
62 }
63 }
64 }
65 }
66
67 fn substitute_path_segment(&self, seg: &mut PathSegment, traitname: &Ident)
68 {
69 match &mut seg.arguments {
70 PathArguments::None => /* nothing to do */(),
71 PathArguments::Parenthesized(p) => {
72 compile_error(p.span(),
73 "Mockall does not support mocking Fn objects. See https://github.com/asomers/mockall/issues/139");
74 },
75 PathArguments::AngleBracketed(abga) => {
76 for arg in abga.args.iter_mut() {
77 match arg {
78 GenericArgument::Type(ty) => {
79 self.substitute_type(ty, traitname)
80 },
81 GenericArgument::Binding(binding) => {
82 self.substitute_type(&mut binding.ty, traitname);
83 },
84 _ => {
85 /*
86 * Nothing to do, as long as lifetimes can't be
87 * associated types
88 */
89 }
90 }
91 }
92 },
93 }
94 }
95
96 /// Recursively substitute types in the input
97 fn substitute_type(&self, ty: &mut Type, traitname: &Ident) {
98 match ty {
99 Type::Slice(s) => {
100 self.substitute_type(s.elem.as_mut(), traitname)
101 },
102 Type::Array(a) => {
103 self.substitute_type(a.elem.as_mut(), traitname)
104 },
105 Type::Ptr(p) => {
106 self.substitute_type(p.elem.as_mut(), traitname)
107 },
108 Type::Reference(r) => {
109 self.substitute_type(r.elem.as_mut(), traitname)
110 },
111 Type::BareFn(bfn) => {
112 for fn_arg in bfn.inputs.iter_mut() {
113 self.substitute_type(&mut fn_arg.ty, traitname);
114 }
115 if let ReturnType::Type(_, ref mut ty) = &mut bfn.output {
116 self.substitute_type(ty, traitname);
117 }
118 },
119 Type::Tuple(tuple) => {
120 for elem in tuple.elems.iter_mut() {
121 self.substitute_type(elem, traitname)
122 }
123 }
124 Type::Path(path) => {
125 if let Some(ref qself) = path.qself {
126 let qp = if let Type::Path(p) = qself.ty.as_ref() {
127 &p.path
128 } else {
129 panic!("QSelf's type isn't a path?")
130 };
131 let qident = &qp.segments.first().unwrap().ident;
132 if qself.position != 1
133 || qp.segments.len() != 1
134 || path.path.segments.len() != 2
135 || qident != "Self" {
136 compile_error(path.span(),
137 "QSelf is a work in progress");
138 }
139
140 let mut seg_iter = path.path.segments.iter().rev();
141 let last_seg = seg_iter.next().unwrap();
142 let to_sub = &last_seg.ident;
143 let penultimate_seg = seg_iter.next().unwrap();
144 let qident = &penultimate_seg.ident;
145 drop(seg_iter);
146
147 if qident != traitname {
148 compile_error(qident.span(),
149 "Mockall does not support QSelf substitutions except for the trait being mocked");
150 }
151 if let Some(new_type) = self.attrs.get(to_sub) {
152 *ty = new_type.clone();
153 } else {
154 compile_error(to_sub.span(),
155 "Unknown type substitution for QSelf");
156 }
157 } else if let Some(newty) = self.get_path(&path.path) {
158 *ty = newty;
159 } else {
160 for seg in path.path.segments.iter_mut() {
161 self.substitute_path_segment(seg, traitname);
162 }
163 }
164 },
165 Type::TraitObject(to) => {
166 for bound in to.bounds.iter_mut() {
167 self.substitute_type_param_bound(bound, traitname);
168 }
169 },
170 Type::ImplTrait(it) => {
171 for bound in it.bounds.iter_mut() {
172 self.substitute_type_param_bound(bound, traitname);
173 }
174 },
175 Type::Paren(p) => {
176 self.substitute_type(p.elem.as_mut(), traitname)
177 },
178 Type::Group(g) => {
179 self.substitute_type(g.elem.as_mut(), traitname)
180 },
181 Type::Macro(_) | Type::Verbatim(_) => {
182 compile_error(ty.span(),
183 "mockall_derive does not support this type when using associated types");
184 },
185 Type::Infer(_) | Type::Never(_) => {
186 /* Nothing to do */
187 },
188 _ => compile_error(ty.span(), "Unsupported type"),
189 }
190 }
191
192 fn substitute_type_param_bound(&self,
193 bound: &mut TypeParamBound,
194 traitname: &Ident)
195 {
196 if let TypeParamBound::Trait(t) = bound {
197 match self.get_path(&t.path) {
198 None => {
199 for seg in t.path.segments.iter_mut() {
200 self.substitute_path_segment(seg, traitname);
201 }
202 },
203 Some(Type::Path(type_path)) => {
204 t.path = type_path.path;
205 },
206 Some(_) => {
207 compile_error(t.path.span(),
208 "Can only substitute paths for trait bounds");
209 }
210 }
211 }
212 }
213
214 pub(crate) fn substitute_trait(&self, item: &ItemTrait) -> ItemTrait {
215 let mut output = item.clone();
216 for trait_item in output.items.iter_mut() {
217 match trait_item {
218 TraitItem::Type(tity) => {
219 if let Some(ty) = self.attrs.get(&tity.ident) {
220 let span = tity.span();
221 tity.default = Some((Token![=](span), ty.clone()));
222 // Concrete associated types aren't allowed to have
223 // bounds
224 tity.bounds = Punctuated::new();
225 } else {
226 compile_error(tity.span(),
227 "Default value not given for associated type");
228 }
229 },
230 TraitItem::Method(method) => {
231 let sig = &mut method.sig;
232 for fn_arg in sig.inputs.iter_mut() {
233 if let FnArg::Typed(arg) = fn_arg {
234 self.substitute_type(&mut arg.ty, &item.ident);
235 }
236 }
237 if let ReturnType::Type(_, ref mut ty) = &mut sig.output {
238 self.substitute_type(ty, &item.ident);
239 }
240 },
241 _ => {
242 // Nothing to do
243 }
244 }
245 }
246 output
247 }
248}
249
250impl Parse for Attrs {
251 fn parse(input: ParseStream) -> parse::Result<Self> {
252 let mut attrs = HashMap::new();
253 let mut modname = None;
254 while !input.is_empty() {
255 let attr: Attr = input.parse()?;
256 match attr {
257 Attr::Mod(item_mod) => {
258 if let Some((br, _)) = item_mod.content {
259 compile_error(br.span,
260 "mod name attributes must have the form \"mod my_name;\"");
261 }
262 modname = Some(item_mod.ident.clone());
263 },
264 Attr::Type(trait_item_type) => {
265 let ident = trait_item_type.ident.clone();
266 if let Some((_, ty)) = trait_item_type.default {
267 attrs.insert(ident, ty.clone());
268 } else {
269 compile_error(trait_item_type.span(),
270 "automock type attributes must have a default value");
271 }
272 }
273 }
274 }
275 Ok(Attrs{attrs, modname})
276 }
277}
278
279/// Unit tests for `Attrs`.
280#[cfg(test)]
281mod t {
282 use super::super::*;
283 use pretty_assertions::assert_eq;
284
285 fn check_substitute_type(
286 attrs: TokenStream,
287 input: TokenStream,
288 traitname: Ident,
289 expected: TokenStream)
290 {
291 let _self: super::Attrs = parse2(attrs).unwrap();
292 let mut in_ty: Type = parse2(input).unwrap();
293 let expect_ty: Type = parse2(expected).unwrap();
294 _self.substitute_type(&mut in_ty, &traitname);
295 assert_eq!(in_ty, expect_ty);
296 }
297
298 #[test]
299 fn qself() {
300 check_substitute_type(quote!(type T = u32;),
301 quote!(<Self as Foo>::T),
302 format_ident!("Foo"),
303 quote!(u32));
304 }
305
306 #[test]
307 #[should_panic(expected = "Mockall does not support QSelf substitutions except for the trait being mocked")]
308 fn qself_other() {
309 check_substitute_type(quote!(type T = u32;),
310 quote!(<Self as AsRef>::T),
311 format_ident!("Foo"),
312 quote!(u32));
313 }
314
315 #[test]
316 #[should_panic(expected = "Unknown type substitution for QSelf")]
317 fn unknown_substitution() {
318 check_substitute_type(quote!(type T = u32;),
319 quote!(<Self as Foo>::Q),
320 format_ident!("Foo"),
321 quote!(u32));
322 }
323}
324