rustc_macros/
visitable.rs1use quote::quote;
2use synstructure::BindingInfo;
3
4pub(super) fn visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
5 if let syn::Data::Union(_) = s.ast().data {
6 panic!("cannot derive on union")
7 }
8
9 let has_attr = |bind: &BindingInfo<'_>, name| {
10 let mut found = false;
11 bind.ast().attrs.iter().for_each(|attr| {
12 if !attr.path().is_ident("visitable") {
13 return;
14 }
15 let _ = attr.parse_nested_meta(|nested| {
16 if nested.path.is_ident(name) {
17 found = true;
18 }
19 Ok(())
20 });
21 });
22 found
23 };
24
25 let get_attr = |bind: &BindingInfo<'_>, name: &str| {
26 let mut content = None;
27 bind.ast().attrs.iter().for_each(|attr| {
28 if !attr.path().is_ident("visitable") {
29 return;
30 }
31 let _ = attr.parse_nested_meta(|nested| {
32 if nested.path.is_ident(name) {
33 let value = nested.value()?;
34 let value = value.parse()?;
35 content = Some(value);
36 }
37 Ok(())
38 });
39 });
40 content
41 };
42
43 s.add_bounds(synstructure::AddBounds::Generics);
44 s.bind_with(|_| synstructure::BindStyle::Ref);
45 let ref_visit = s.each(|bind| {
46 let extra = get_attr(bind, "extra").unwrap_or(quote! {});
47 if has_attr(bind, "ignore") {
48 quote! {}
49 } else {
50 quote! { rustc_ast_ir::try_visit!(crate::visit::Visitable::visit(#bind, __visitor, (#extra))) }
51 }
52 });
53
54 s.bind_with(|_| synstructure::BindStyle::RefMut);
55 let mut_visit = s.each(|bind| {
56 let extra = get_attr(bind, "extra").unwrap_or(quote! {});
57 if has_attr(bind, "ignore") {
58 quote! {}
59 } else {
60 quote! { crate::mut_visit::MutVisitable::visit_mut(#bind, __visitor, (#extra)) }
61 }
62 });
63
64 s.gen_impl(quote! {
65 gen impl<'__ast, __V> crate::visit::Walkable<'__ast, __V> for @Self
66 where __V: crate::visit::Visitor<'__ast>,
67 {
68 fn walk_ref(&'__ast self, __visitor: &mut __V) -> __V::Result {
69 match *self { #ref_visit }
70 <__V::Result as rustc_ast_ir::visit::VisitorResult>::output()
71 }
72 }
73
74 gen impl<__V> crate::mut_visit::MutWalkable<__V> for @Self
75 where __V: crate::mut_visit::MutVisitor,
76 {
77 fn walk_mut(&mut self, __visitor: &mut __V) {
78 match *self { #mut_visit }
79 }
80 }
81 })
82}