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