@@ -316,29 +316,14 @@ pub(crate) fn generate(
316316 } ;
317317
318318 //
319- // wrap the function body in a closure
319+ // wrap the function body in a closure if we have any postconditions
320320 //
321321
322- let block = func. function . block . clone ( ) ;
323-
324- let ret_ty = match & func. function . sig . output {
325- ReturnType :: Type ( _, ty) => {
326- let span = ty. span ( ) ;
327- match ty. as_ref ( ) {
328- syn:: Type :: ImplTrait ( _) => quote:: quote! { } ,
329- ty => quote:: quote_spanned! { span=>
330- -> #ty
331- } ,
332- }
333- }
334- ReturnType :: Default => quote:: quote! { } ,
335- } ;
336-
337- let body = quote:: quote! {
338- #[ allow( unused_mut) ]
339- let mut run = || #ret_ty #block;
340-
341- let ret = run( ) ;
322+ let body = if post. is_empty ( ) {
323+ let block = & func. function . block ;
324+ quote:: quote! { let ret = #block; }
325+ } else {
326+ create_closure_body_and_adjust_signature ( & func. function )
342327 } ;
343328
344329 //
@@ -371,3 +356,157 @@ pub(crate) fn generate(
371356
372357 func. function . into_token_stream ( )
373358}
359+
360+ struct SelfReplacer < ' a > {
361+ replace_with : & ' a syn:: Ident ,
362+ }
363+
364+ impl syn:: visit_mut:: VisitMut for SelfReplacer < ' _ > {
365+ fn visit_ident_mut ( & mut self , i : & mut Ident ) {
366+ if i == "self" {
367+ * i = self . replace_with . clone ( ) ;
368+ }
369+ }
370+ }
371+
372+ fn ty_contains_impl_trait ( ty : & syn:: Type ) -> bool {
373+ use syn:: visit:: Visit ;
374+
375+ struct TyContainsImplTrait {
376+ seen_impl_trait : bool ,
377+ }
378+
379+ impl syn:: visit:: Visit < ' _ > for TyContainsImplTrait {
380+ fn visit_type_impl_trait ( & mut self , _: & syn:: TypeImplTrait ) {
381+ self . seen_impl_trait = true ;
382+ }
383+ }
384+
385+ let mut vis = TyContainsImplTrait {
386+ seen_impl_trait : false ,
387+ } ;
388+ vis. visit_type ( ty) ;
389+ vis. seen_impl_trait
390+ }
391+
392+ fn create_closure_body_and_adjust_signature ( func : & syn:: ItemFn ) -> TokenStream {
393+ let is_method = func. sig . receiver ( ) . is_some ( ) ;
394+
395+ // If the function has a receiver (e.g. `self`, `&mut self`, or `self: Box<Self>`) rename it
396+ // to `this__` within the closure
397+
398+ let mut block = func. block . clone ( ) ;
399+ let mut closure_args = vec ! [ ] ;
400+ let mut arg_names = vec ! [ ] ;
401+
402+ if is_method {
403+ let this_ident = syn:: Ident :: new ( "this__" , Span :: call_site ( ) ) ;
404+
405+ let mut receiver = func. sig . inputs [ 0 ] . clone ( ) ;
406+ match receiver {
407+ // self, &self, &mut self
408+ syn:: FnArg :: Receiver ( rcv) => {
409+ // The `Self` type.
410+ let self_ty = Box :: new ( syn:: Type :: Path ( syn:: TypePath {
411+ qself : None ,
412+ path : syn:: Path :: from ( syn:: Ident :: new ( "Self" , rcv. span ( ) ) ) ,
413+ } ) ) ;
414+
415+ let ty = if let Some ( ( and_token, ref lifetime) ) = rcv. reference
416+ {
417+ Box :: new ( syn:: Type :: Reference ( syn:: TypeReference {
418+ and_token,
419+ lifetime : lifetime. clone ( ) ,
420+ mutability : rcv. mutability ,
421+ elem : self_ty,
422+ } ) )
423+ } else {
424+ self_ty
425+ } ;
426+
427+ let pat_mut = if rcv. reference . is_none ( ) {
428+ rcv. mutability
429+ } else {
430+ None
431+ } ;
432+
433+ // this__: [& [mut]] Self
434+ let new_rcv = syn:: PatType {
435+ attrs : rcv. attrs . clone ( ) ,
436+ pat : Box :: new ( syn:: Pat :: Ident ( syn:: PatIdent {
437+ attrs : vec ! [ ] ,
438+ by_ref : None ,
439+ mutability : pat_mut,
440+ ident : this_ident. clone ( ) ,
441+ subpat : None ,
442+ } ) ) ,
443+ colon_token : syn:: Token ![ : ] ( rcv. span ( ) ) ,
444+ ty,
445+ } ;
446+
447+ receiver = syn:: FnArg :: Typed ( new_rcv) ;
448+ }
449+
450+ // self: Box<Self>
451+ syn:: FnArg :: Typed ( ref mut pat) => {
452+ if let syn:: Pat :: Ident ( ref mut ident) = * pat. pat {
453+ if ident. ident == "self" {
454+ ident. ident = this_ident. clone ( ) ;
455+ }
456+ }
457+ }
458+ }
459+
460+ closure_args. push ( receiver) ;
461+ arg_names. push ( syn:: Ident :: new ( "self" , Span :: call_site ( ) ) ) ;
462+
463+ // Replace any references to `self` in the function body with references to `this__`.
464+ syn:: visit_mut:: visit_block_mut (
465+ & mut SelfReplacer {
466+ replace_with : & this_ident,
467+ } ,
468+ & mut block,
469+ ) ;
470+ }
471+
472+ // Replace any pattern matching in the function signature with placeholder identifiers.
473+ // Pattern matching gets done in the closure instead.
474+ let args = func. sig . inputs . iter ( ) . skip ( if is_method { 1 } else { 0 } ) ;
475+ for arg in args {
476+ match arg {
477+ syn:: FnArg :: Receiver ( _) => unreachable ! ( "Multiple receivers?" ) ,
478+
479+ syn:: FnArg :: Typed ( syn:: PatType { pat, ty, .. } ) => {
480+ if !ty_contains_impl_trait ( ty) {
481+ if let syn:: Pat :: Ident ( ident) = & * * pat {
482+ arg_names. push ( ident. ident . clone ( ) ) ;
483+ closure_args. push ( arg. clone ( ) ) ;
484+ }
485+ }
486+ }
487+ }
488+ }
489+
490+ let ret_ty = match & func. sig . output {
491+ ReturnType :: Type ( _, ty) => {
492+ let span = ty. span ( ) ;
493+ match ty. as_ref ( ) {
494+ syn:: Type :: ImplTrait ( _) => quote:: quote! { } ,
495+ ty => quote:: quote_spanned! { span=>
496+ -> #ty
497+ } ,
498+ }
499+ }
500+ ReturnType :: Default => quote:: quote! { } ,
501+ } ;
502+
503+ let closure_args = closure_args. iter ( ) ;
504+ let arg_names = arg_names. iter ( ) ;
505+
506+ quote:: quote! {
507+ #[ allow( unused_mut) ]
508+ let mut run = |#( #closure_args) , * | #ret_ty #block;
509+
510+ let ret = run( #( #arg_names) , * ) ;
511+ }
512+ }
0 commit comments