1 | // vim: tw=80 |
2 | use super::*; |
3 | use syn::parse::{Parse, ParseStream}; |
4 | |
5 | /// Make any implicit lifetime parameters explicit |
6 | fn add_lifetime_parameters(sig: &mut Signature) { |
7 | fn add_to_trait_object(generics: &mut Generics, var: &Pat, to: &mut TypeTraitObject) { |
8 | let mut has_lifetime = false; |
9 | for bound in to.bounds.iter() { |
10 | if let TypeParamBound::Lifetime(_) = bound { |
11 | has_lifetime = true; |
12 | } |
13 | } |
14 | if ! has_lifetime { |
15 | let arg_ident = match *var { |
16 | Pat::Wild(_) => { |
17 | compile_error(var.span(), |
18 | "Mocked methods must have named arguments" ); |
19 | format_ident!("dont_care" ) |
20 | }, |
21 | Pat::Ident(ref pat_ident) => { |
22 | if let Some(r) = &pat_ident.by_ref { |
23 | compile_error(r.span(), |
24 | "Mockall does not support by-reference argument bindings" ); |
25 | } |
26 | if let Some((_at, subpat)) = &pat_ident.subpat { |
27 | compile_error(subpat.span(), |
28 | "Mockall does not support subpattern bindings" ); |
29 | } |
30 | pat_ident.ident.clone() |
31 | }, |
32 | _ => { |
33 | compile_error(var.span(), |
34 | "Unsupported argument type" ); |
35 | format_ident!("dont_care" ) |
36 | } |
37 | }; |
38 | let s = format!("'__mockall_{}" , arg_ident); |
39 | let span = Span::call_site(); |
40 | let lt = Lifetime::new(&s, span); |
41 | to.bounds.push(TypeParamBound::Lifetime(lt.clone())); |
42 | generics.lt_token.get_or_insert(Token![<](span)); |
43 | generics.gt_token.get_or_insert(Token![>](span)); |
44 | let gpl = GenericParam::Lifetime(LifetimeDef::new(lt)); |
45 | generics.params.push(gpl); |
46 | } |
47 | } |
48 | |
49 | fn add_to_type(generics: &mut Generics, var: &Pat, ty: &mut Type) { |
50 | match ty { |
51 | Type::Array(ta) => add_to_type(generics, var, ta.elem.as_mut()), |
52 | Type::BareFn(_) => (), |
53 | Type::ImplTrait(_) => (), |
54 | Type::Path(_) => (), |
55 | Type::Ptr(_) => (), |
56 | Type::Reference(tr) => { |
57 | match tr.elem.as_mut() { |
58 | Type::Paren(tp) => { |
59 | if let Type::TraitObject(to) = tp.elem.as_mut() { |
60 | add_to_trait_object(generics, var, to); |
61 | } else { |
62 | add_to_type(generics, var, tr.elem.as_mut()); |
63 | } |
64 | }, |
65 | Type::TraitObject(to) => { |
66 | add_to_trait_object(generics, var, to); |
67 | // We need to wrap it in a Paren. Otherwise it won't be |
68 | // syntactically valid after we add a lifetime bound, |
69 | // due to a "ambiguous `+` in a type" error |
70 | *tr.elem = Type::Paren(TypeParen { |
71 | paren_token: token::Paren::default(), |
72 | elem: Box::new(Type::TraitObject(to.clone())) |
73 | }); |
74 | }, |
75 | _ => add_to_type(generics, var, tr.elem.as_mut()), |
76 | } |
77 | }, |
78 | Type::Slice(ts) => add_to_type(generics, var, ts.elem.as_mut()), |
79 | Type::Tuple(tt) => { |
80 | for ty in tt.elems.iter_mut() { |
81 | add_to_type(generics, var, ty) |
82 | } |
83 | }, |
84 | _ => compile_error(ty.span(), "unsupported type in this position" ) |
85 | } |
86 | } |
87 | |
88 | for arg in sig.inputs.iter_mut() { |
89 | if let FnArg::Typed(pt) = arg { |
90 | add_to_type(&mut sig.generics, &pt.pat, &mut pt.ty) |
91 | } |
92 | } |
93 | } |
94 | |
95 | /// Generate a #[derive(Debug)] Attribute |
96 | fn derive_debug() -> Attribute { |
97 | Attribute { |
98 | pound_token: <Token![#]>::default(), |
99 | style: AttrStyle::Outer, |
100 | bracket_token: token::Bracket::default(), |
101 | path: Path::from(format_ident!("derive" )), |
102 | tokens: quote!((Debug)) |
103 | } |
104 | } |
105 | |
106 | /// Add "Mock" to the front of the named type |
107 | fn mock_ident_in_type(ty: &mut Type) { |
108 | match ty { |
109 | Type::Path(type_path) => { |
110 | if type_path.path.segments.len() != 1 { |
111 | compile_error(type_path.path.span(), |
112 | "mockall_derive only supports structs defined in the current module" ); |
113 | return; |
114 | } |
115 | let ident = &mut type_path.path.segments.last_mut().unwrap().ident; |
116 | *ident = gen_mock_ident(ident) |
117 | }, |
118 | x => { |
119 | compile_error(x.span(), |
120 | "mockall_derive only supports mocking traits and structs" ); |
121 | } |
122 | }; |
123 | } |
124 | |
125 | /// Performs transformations on the ItemImpl to make it mockable |
126 | fn mockable_item_impl(mut impl_: ItemImpl, name: &Ident, generics: &Generics) |
127 | -> ItemImpl |
128 | { |
129 | mock_ident_in_type(&mut impl_.self_ty); |
130 | for item in impl_.items.iter_mut() { |
131 | if let ImplItem::Method(ref mut iim) = item { |
132 | mockable_method(iim, name, generics); |
133 | } |
134 | } |
135 | impl_ |
136 | } |
137 | |
138 | /// Performs transformations on the method to make it mockable |
139 | fn mockable_method(meth: &mut ImplItemMethod, name: &Ident, generics: &Generics) |
140 | { |
141 | demutify(&mut meth.sig.inputs); |
142 | deselfify_args(&mut meth.sig.inputs, name, generics); |
143 | add_lifetime_parameters(&mut meth.sig); |
144 | deimplify(&mut meth.sig.output); |
145 | dewhereselfify(&mut meth.sig.generics); |
146 | if let ReturnType::Type(_, ty) = &mut meth.sig.output { |
147 | deselfify(ty, name, generics); |
148 | deanonymize(ty); |
149 | } |
150 | sanity_check_sig(&meth.sig); |
151 | } |
152 | |
153 | /// Performs transformations on the method to make it mockable |
154 | fn mockable_trait_method( |
155 | meth: &mut TraitItemMethod, |
156 | name: &Ident, |
157 | generics: &Generics) |
158 | { |
159 | demutify(&mut meth.sig.inputs); |
160 | deselfify_args(&mut meth.sig.inputs, name, generics); |
161 | add_lifetime_parameters(&mut meth.sig); |
162 | deimplify(&mut meth.sig.output); |
163 | dewhereselfify(&mut meth.sig.generics); |
164 | if let ReturnType::Type(_, ty) = &mut meth.sig.output { |
165 | deselfify(ty, name, generics); |
166 | deanonymize(ty); |
167 | } |
168 | sanity_check_sig(&meth.sig); |
169 | } |
170 | |
171 | /// Generates a mockable item impl from a trait method definition |
172 | fn mockable_trait(trait_: ItemTrait, name: &Ident, generics: &Generics) |
173 | -> ItemImpl |
174 | { |
175 | let items = trait_.items.into_iter() |
176 | .map(|ti| { |
177 | match ti { |
178 | TraitItem::Method(mut tim) => { |
179 | mockable_trait_method(&mut tim, name, generics); |
180 | ImplItem::Method(tim2iim(tim, &Visibility::Inherited)) |
181 | }, |
182 | TraitItem::Const(tic) => { |
183 | ImplItem::Const(tic2iic(tic, &Visibility::Inherited)) |
184 | }, |
185 | TraitItem::Type(tit) => { |
186 | ImplItem::Type(tit2iit(tit, &Visibility::Inherited)) |
187 | }, |
188 | _ => { |
189 | compile_error(ti.span(), "Unsupported in this context" ); |
190 | ImplItem::Verbatim(TokenStream::new()) |
191 | } |
192 | } |
193 | }).collect::<Vec<_>>(); |
194 | let mut trait_path = Path::from(trait_.ident); |
195 | let mut struct_path = Path::from(name.clone()); |
196 | let (_, stg, _) = generics.split_for_impl(); |
197 | let (_, ttg, _) = trait_.generics.split_for_impl(); |
198 | if let Ok(abga) = parse2::<AngleBracketedGenericArguments>(quote!(#stg)) { |
199 | struct_path.segments.last_mut().unwrap().arguments = |
200 | PathArguments::AngleBracketed(abga); |
201 | } |
202 | if let Ok(abga) = parse2::<AngleBracketedGenericArguments>(quote!(#ttg)) { |
203 | trait_path.segments.last_mut().unwrap().arguments = |
204 | PathArguments::AngleBracketed(abga); |
205 | } |
206 | let self_ty = Box::new(Type::Path(TypePath{ |
207 | qself: None, |
208 | path: struct_path, |
209 | })); |
210 | ItemImpl { |
211 | attrs: trait_.attrs, |
212 | defaultness: None, |
213 | unsafety: trait_.unsafety, |
214 | impl_token: <Token![impl]>::default(), |
215 | generics: generics.clone(), |
216 | trait_: Some((None, trait_path, <Token![for]>::default())), |
217 | self_ty, |
218 | brace_token: trait_.brace_token, |
219 | items |
220 | } |
221 | } |
222 | |
223 | fn sanity_check_sig(sig: &Signature) { |
224 | for arg in sig.inputs.iter() { |
225 | if let FnArg::Typed(pt) = arg { |
226 | if let Type::ImplTrait(it) = pt.ty.as_ref() { |
227 | let bounds = &it.bounds; |
228 | let s = format!( |
229 | "Mockall does not support \"impl trait \" in argument position. Use \"T: {} \" instead" , |
230 | quote!(#bounds) |
231 | ); |
232 | compile_error(it.span(), &s); |
233 | } |
234 | } |
235 | } |
236 | } |
237 | |
238 | /// Converts a TraitItemConst into an ImplItemConst |
239 | fn tic2iic(tic: TraitItemConst, vis: &syn::Visibility) -> ImplItemConst { |
240 | let span = tic.span(); |
241 | let (eq_token, expr) = tic.default.unwrap_or_else(|| { |
242 | compile_error(span, |
243 | "Mocked associated consts must have a default implementation" ); |
244 | (<Token![=]>::default(), Expr::Verbatim(TokenStream::new())) |
245 | }); |
246 | ImplItemConst { |
247 | attrs: tic.attrs, |
248 | vis: vis.clone(), |
249 | defaultness: None, |
250 | const_token: tic.const_token, |
251 | ident: tic.ident, |
252 | colon_token: tic.colon_token, |
253 | ty: tic.ty, |
254 | eq_token, |
255 | expr, |
256 | semi_token: tic.semi_token |
257 | } |
258 | } |
259 | |
260 | /// Converts a TraitItemMethod into an ImplItemMethod |
261 | fn tim2iim(m: syn::TraitItemMethod, vis: &syn::Visibility) |
262 | -> syn::ImplItemMethod |
263 | { |
264 | let empty_block = Block { |
265 | brace_token: token::Brace::default(), |
266 | stmts: Vec::new() |
267 | }; |
268 | syn::ImplItemMethod{ |
269 | attrs: m.attrs, |
270 | vis: vis.clone(), |
271 | defaultness: None, |
272 | sig: m.sig, |
273 | block: empty_block |
274 | } |
275 | } |
276 | |
277 | /// Converts a TraitItemType into an ImplItemType |
278 | fn tit2iit(tit: TraitItemType, vis: &Visibility) -> ImplItemType { |
279 | let span = tit.span(); |
280 | let (eq_token, ty) = tit.default.unwrap_or_else(|| { |
281 | compile_error(span, |
282 | "associated types in mock! must be fully specified" ); |
283 | (token::Eq::default(), Type::Verbatim(TokenStream::new())) |
284 | }); |
285 | ImplItemType { |
286 | attrs: tit.attrs, |
287 | vis: vis.clone(), |
288 | defaultness: None, |
289 | type_token: tit.type_token, |
290 | ident: tit.ident, |
291 | generics: tit.generics, |
292 | eq_token, |
293 | ty, |
294 | semi_token: tit.semi_token, |
295 | } |
296 | } |
297 | |
298 | pub(crate) struct MockableStruct { |
299 | pub attrs: Vec<Attribute>, |
300 | pub consts: Vec<ImplItemConst>, |
301 | pub generics: Generics, |
302 | /// Inherent methods of the mockable struct |
303 | pub methods: Vec<ImplItemMethod>, |
304 | pub name: Ident, |
305 | pub vis: Visibility, |
306 | pub impls: Vec<ItemImpl> |
307 | } |
308 | |
309 | impl MockableStruct { |
310 | /// Does this struct derive Debug? |
311 | pub fn derives_debug(&self) -> bool { |
312 | self.attrs.iter() |
313 | .any(|attr| { |
314 | if let Ok(Meta::List(ml)) = attr.parse_meta() { |
315 | let i = ml.path.get_ident(); |
316 | if i.map_or(false, |i| *i == "derive" ) { |
317 | ml.nested.iter() |
318 | .any(|nm| { |
319 | if let NestedMeta::Meta(m) = nm { |
320 | let i = m.path().get_ident(); |
321 | i.map_or(false, |i| *i == "Debug" ) |
322 | } else { |
323 | false |
324 | } |
325 | }) |
326 | } else { |
327 | false |
328 | } |
329 | } else { |
330 | false |
331 | } |
332 | }) |
333 | } |
334 | } |
335 | |
336 | impl From<(Attrs, ItemTrait)> for MockableStruct { |
337 | fn from((attrs, item_trait): (Attrs, ItemTrait)) -> MockableStruct { |
338 | let trait_ = attrs.substitute_trait(&item_trait); |
339 | let mut attrs = trait_.attrs.clone(); |
340 | attrs.push(derive_debug()); |
341 | let vis = trait_.vis.clone(); |
342 | let name = gen_mock_ident(&trait_.ident); |
343 | let generics = trait_.generics.clone(); |
344 | let impls = vec![mockable_trait(trait_, &name, &generics)]; |
345 | MockableStruct { |
346 | attrs, |
347 | consts: Vec::new(), |
348 | vis, |
349 | name, |
350 | generics, |
351 | methods: Vec::new(), |
352 | impls |
353 | } |
354 | } |
355 | } |
356 | |
357 | impl From<ItemImpl> for MockableStruct { |
358 | fn from(mut item_impl: ItemImpl) -> MockableStruct { |
359 | let name = match &*item_impl.self_ty { |
360 | Type::Path(type_path) => { |
361 | let n = find_ident_from_path(&type_path.path).0; |
362 | gen_mock_ident(&n) |
363 | }, |
364 | x => { |
365 | compile_error(x.span(), |
366 | "mockall_derive only supports mocking traits and structs" ); |
367 | Ident::new("" , Span::call_site()) |
368 | } |
369 | }; |
370 | let mut attrs = item_impl.attrs.clone(); |
371 | attrs.push(derive_debug()); |
372 | let mut consts = Vec::new(); |
373 | let generics = item_impl.generics.clone(); |
374 | let mut methods = Vec::new(); |
375 | let pub_token = Token![pub](Span::call_site()); |
376 | let vis = Visibility::Public(VisPublic{pub_token}); |
377 | let mut impls = Vec::new(); |
378 | if let Some((bang, _path, _)) = &item_impl.trait_ { |
379 | if bang.is_some() { |
380 | compile_error(bang.span(), "Unsupported by automock" ); |
381 | } |
382 | |
383 | // Substitute any associated types in this ItemImpl. |
384 | // NB: this would not be necessary if the user always fully |
385 | // qualified them, e.g. `<Self as MyTrait>::MyType` |
386 | let mut attrs = Attrs::default(); |
387 | for item in item_impl.items.iter() { |
388 | match item { |
389 | ImplItem::Const(_iic) => |
390 | (), |
391 | ImplItem::Method(_meth) => |
392 | (), |
393 | ImplItem::Type(ty) => { |
394 | attrs.attrs.insert(ty.ident.clone(), ty.ty.clone()); |
395 | }, |
396 | x => compile_error(x.span(), "Unsupported by automock" ) |
397 | } |
398 | } |
399 | attrs.substitute_item_impl(&mut item_impl); |
400 | impls.push(mockable_item_impl(item_impl, &name, &generics)); |
401 | } else { |
402 | for item in item_impl.items.into_iter() { |
403 | match item { |
404 | ImplItem::Method(mut meth) => { |
405 | mockable_method(&mut meth, &name, &item_impl.generics); |
406 | methods.push(meth) |
407 | }, |
408 | ImplItem::Const(iic) => consts.push(iic), |
409 | // Rust doesn't allow types in an inherent impl |
410 | x => compile_error(x.span(), |
411 | "Unsupported by Mockall in this context" ), |
412 | } |
413 | } |
414 | }; |
415 | MockableStruct { |
416 | attrs, |
417 | consts, |
418 | generics, |
419 | methods, |
420 | name, |
421 | vis, |
422 | impls, |
423 | } |
424 | } |
425 | } |
426 | |
427 | impl Parse for MockableStruct { |
428 | fn parse(input: ParseStream) -> syn::parse::Result<Self> { |
429 | let attrs = input.call(syn::Attribute::parse_outer)?; |
430 | let vis: syn::Visibility = input.parse()?; |
431 | let original_name: syn::Ident = input.parse()?; |
432 | let mut generics: syn::Generics = input.parse()?; |
433 | let wc: Option<syn::WhereClause> = input.parse()?; |
434 | generics.where_clause = wc; |
435 | let name = gen_mock_ident(&original_name); |
436 | let impl_content; |
437 | let _brace_token = braced!(impl_content in input); |
438 | let mut consts = Vec::new(); |
439 | let mut methods = Vec::new(); |
440 | while !impl_content.is_empty() { |
441 | let item: ImplItem = impl_content.parse()?; |
442 | match item { |
443 | ImplItem::Method(mut iim) => { |
444 | mockable_method(&mut iim, &name, &generics); |
445 | methods.push(iim); |
446 | }, |
447 | ImplItem::Const(iic) => consts.push(iic), |
448 | _ => { |
449 | return Err(input.error("Unsupported in this context" )); |
450 | } |
451 | } |
452 | } |
453 | |
454 | let mut impls = Vec::new(); |
455 | while !input.is_empty() { |
456 | let item: Item = input.parse()?; |
457 | match item { |
458 | Item::Trait(it) => { |
459 | let note = "Deprecated mock! syntax. Instead of \"trait X \", write \"impl X for Y \". See PR #205" ; |
460 | let mut impl_ = mockable_trait(it, &name, &generics); |
461 | impl_.attrs.push(Attribute { |
462 | pound_token: <token::Pound>::default(), |
463 | style: AttrStyle::Outer, |
464 | bracket_token: token::Bracket::default(), |
465 | path: Path::from(format_ident!("deprecated" )), |
466 | tokens: quote!((since = "0.9.0" , note = #note)), |
467 | }); |
468 | impls.push(impl_) |
469 | }, |
470 | Item::Impl(ii) => |
471 | impls.push(mockable_item_impl(ii, &name, &generics)), |
472 | _ => return Err(input.error("Unsupported in this context" )), |
473 | } |
474 | } |
475 | |
476 | Ok( |
477 | MockableStruct { |
478 | attrs, |
479 | consts, |
480 | generics, |
481 | methods, |
482 | name, |
483 | vis, |
484 | impls |
485 | } |
486 | ) |
487 | } |
488 | } |
489 | |
490 | #[cfg (test)] |
491 | mod t { |
492 | use super::*; |
493 | |
494 | mod add_lifetime_parameters { |
495 | use super::*; |
496 | |
497 | #[test] |
498 | fn array() { |
499 | let mut meth: TraitItemMethod = parse2(quote!( |
500 | fn foo(&self, x: [&dyn T; 1]); |
501 | )).unwrap(); |
502 | add_lifetime_parameters(&mut meth.sig); |
503 | assert_eq!( |
504 | quote!(fn foo<'__mockall_x>(&self, x: [&(dyn T + '__mockall_x); 1]);) |
505 | .to_string(), |
506 | quote!(#meth).to_string() |
507 | ); |
508 | } |
509 | |
510 | #[test] |
511 | fn bare_fn_with_named_args() { |
512 | let mut meth: TraitItemMethod = parse2(quote!( |
513 | fn foo(&self, x: fn(&dyn T)); |
514 | )).unwrap(); |
515 | add_lifetime_parameters(&mut meth.sig); |
516 | assert_eq!( |
517 | quote!(fn foo(&self, x: fn(&dyn T));).to_string(), |
518 | quote!(#meth).to_string() |
519 | ); |
520 | } |
521 | |
522 | #[test] |
523 | fn plain() { |
524 | let mut meth: TraitItemMethod = parse2(quote!( |
525 | fn foo(&self, x: &dyn T); |
526 | )).unwrap(); |
527 | add_lifetime_parameters(&mut meth.sig); |
528 | assert_eq!( |
529 | quote!(fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x));) |
530 | .to_string(), |
531 | quote!(#meth).to_string() |
532 | ); |
533 | } |
534 | |
535 | #[test] |
536 | fn slice() { |
537 | let mut meth: TraitItemMethod = parse2(quote!( |
538 | fn foo(&self, x: &[&dyn T]); |
539 | )).unwrap(); |
540 | add_lifetime_parameters(&mut meth.sig); |
541 | assert_eq!( |
542 | quote!(fn foo<'__mockall_x>(&self, x: &[&(dyn T + '__mockall_x)]);) |
543 | .to_string(), |
544 | quote!(#meth).to_string() |
545 | ); |
546 | } |
547 | |
548 | #[test] |
549 | fn tuple() { |
550 | let mut meth: TraitItemMethod = parse2(quote!( |
551 | fn foo(&self, x: (&dyn T, u32)); |
552 | )).unwrap(); |
553 | add_lifetime_parameters(&mut meth.sig); |
554 | assert_eq!( |
555 | quote!(fn foo<'__mockall_x>(&self, x: (&(dyn T + '__mockall_x), u32));) |
556 | .to_string(), |
557 | quote!(#meth).to_string() |
558 | ); |
559 | } |
560 | |
561 | #[test] |
562 | fn with_anonymous_lifetime() { |
563 | let mut meth: TraitItemMethod = parse2(quote!( |
564 | fn foo(&self, x: &(dyn T + '_)); |
565 | )).unwrap(); |
566 | add_lifetime_parameters(&mut meth.sig); |
567 | assert_eq!( |
568 | quote!(fn foo(&self, x: &(dyn T + '_));).to_string(), |
569 | quote!(#meth).to_string() |
570 | ); |
571 | } |
572 | |
573 | #[test] |
574 | fn with_parens() { |
575 | let mut meth: TraitItemMethod = parse2(quote!( |
576 | fn foo(&self, x: &(dyn T)); |
577 | )).unwrap(); |
578 | add_lifetime_parameters(&mut meth.sig); |
579 | assert_eq!( |
580 | quote!(fn foo<'__mockall_x>(&self, x: &(dyn T + '__mockall_x));) |
581 | .to_string(), |
582 | quote!(#meth).to_string() |
583 | ); |
584 | } |
585 | |
586 | #[test] |
587 | fn with_lifetime_parameter() { |
588 | let mut meth: TraitItemMethod = parse2(quote!( |
589 | fn foo<'a>(&self, x: &(dyn T + 'a)); |
590 | )).unwrap(); |
591 | add_lifetime_parameters(&mut meth.sig); |
592 | assert_eq!( |
593 | quote!(fn foo<'a>(&self, x: &(dyn T + 'a));).to_string(), |
594 | quote!(#meth).to_string() |
595 | ); |
596 | } |
597 | |
598 | #[test] |
599 | fn with_static_lifetime() { |
600 | let mut meth: TraitItemMethod = parse2(quote!( |
601 | fn foo(&self, x: &(dyn T + 'static)); |
602 | )).unwrap(); |
603 | add_lifetime_parameters(&mut meth.sig); |
604 | assert_eq!( |
605 | quote!(fn foo(&self, x: &(dyn T + 'static));).to_string(), |
606 | quote!(#meth).to_string() |
607 | ); |
608 | } |
609 | |
610 | } |
611 | |
612 | mod sanity_check_sig { |
613 | use super::*; |
614 | |
615 | #[test] |
616 | #[should_panic (expected = "Mockall does not support \"impl trait \" in argument position. Use \"T: SomeTrait \" instead." )] |
617 | fn impl_trait() { |
618 | let meth: ImplItemMethod = parse2(quote!( |
619 | fn foo(&self, x: impl SomeTrait); |
620 | )).unwrap(); |
621 | sanity_check_sig(&meth.sig); |
622 | } |
623 | } |
624 | } |
625 | |