| use quote::quote; | |
| use synstructure::BindingInfo; | |
| pub(super) fn visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { | |
| if let syn::Data::Union(_) = s.ast().data { | |
| panic!("cannot derive on union") | |
| } | |
| let has_attr = |bind: &BindingInfo<'_>, name| { | |
| let mut found = false; | |
| bind.ast().attrs.iter().for_each(|attr| { | |
| if !attr.path().is_ident("visitable") { | |
| return; | |
| } | |
| let _ = attr.parse_nested_meta(|nested| { | |
| if nested.path.is_ident(name) { | |
| found = true; | |
| } | |
| Ok(()) | |
| }); | |
| }); | |
| found | |
| }; | |
| let get_attr = |bind: &BindingInfo<'_>, name: &str| { | |
| let mut content = None; | |
| bind.ast().attrs.iter().for_each(|attr| { | |
| if !attr.path().is_ident("visitable") { | |
| return; | |
| } | |
| let _ = attr.parse_nested_meta(|nested| { | |
| if nested.path.is_ident(name) { | |
| let value = nested.value()?; | |
| let value = value.parse()?; | |
| content = Some(value); | |
| } | |
| Ok(()) | |
| }); | |
| }); | |
| content | |
| }; | |
| s.add_bounds(synstructure::AddBounds::Generics); | |
| s.bind_with(|_| synstructure::BindStyle::Ref); | |
| let ref_visit = s.each(|bind| { | |
| let extra = get_attr(bind, "extra").unwrap_or(quote! {}); | |
| if has_attr(bind, "ignore") { | |
| quote! {} | |
| } else { | |
| quote! { rustc_ast_ir::try_visit!(crate::visit::Visitable::visit(#bind, __visitor, (#extra))) } | |
| } | |
| }); | |
| s.bind_with(|_| synstructure::BindStyle::RefMut); | |
| let mut_visit = s.each(|bind| { | |
| let extra = get_attr(bind, "extra").unwrap_or(quote! {}); | |
| if has_attr(bind, "ignore") { | |
| quote! {} | |
| } else { | |
| quote! { crate::mut_visit::MutVisitable::visit_mut(#bind, __visitor, (#extra)) } | |
| } | |
| }); | |
| s.gen_impl(quote! { | |
| gen impl<'__ast, __V> crate::visit::Walkable<'__ast, __V> for @Self | |
| where __V: crate::visit::Visitor<'__ast>, | |
| { | |
| fn walk_ref(&'__ast self, __visitor: &mut __V) -> __V::Result { | |
| match *self { #ref_visit } | |
| <__V::Result as rustc_ast_ir::visit::VisitorResult>::output() | |
| } | |
| } | |
| gen impl<__V> crate::mut_visit::MutWalkable<__V> for @Self | |
| where __V: crate::mut_visit::MutVisitor, | |
| { | |
| fn walk_mut(&mut self, __visitor: &mut __V) { | |
| match *self { #mut_visit } | |
| } | |
| } | |
| }) | |
| } |