Skip to main content

rustc_macros/
visitable.rs

1use 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}