use quote::{quote, ToTokens}; use syn::{parse_quote, Attribute, Meta, NestedMeta}; pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { if let syn::Data::Union(_) = s.ast().data { panic!("cannot derive on union") } if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { s.add_impl_generic(parse_quote! { 'tcx }); } s.add_bounds(synstructure::AddBounds::Generics); s.bind_with(|_| synstructure::BindStyle::Move); let body_fold = s.each_variant(|vi| { let bindings = vi.bindings(); vi.construct(|_, index| { let bind = &bindings[index]; // retain value of fields with #[type_foldable(identity)] let fixed = bind .ast() .attrs .iter() .map(Attribute::parse_meta) .filter_map(Result::ok) .flat_map(|attr| match attr { Meta::List(list) if list.path.is_ident("type_foldable") => list.nested, _ => Default::default(), }) .any(|nested| match nested { NestedMeta::Meta(Meta::Path(path)) => path.is_ident("identity"), _ => false, }); if fixed { bind.to_token_stream() } else { quote! { ::rustc_middle::ty::fold::TypeFoldable::try_fold_with(#bind, __folder)? } } }) }); s.bound_impl( quote!(::rustc_middle::ty::fold::TypeFoldable<::rustc_middle::ty::TyCtxt<'tcx>>), quote! { fn try_fold_with<__F: ::rustc_middle::ty::fold::FallibleTypeFolder<::rustc_middle::ty::TyCtxt<'tcx>>>( self, __folder: &mut __F ) -> Result { Ok(match self { #body_fold }) } }, ) }