bon_macros/normalization/cfg/
visit.rs

1use crate::util::prelude::*;
2
3pub(crate) fn visit_attrs(
4    item: &mut syn::Item,
5    visit: impl FnMut(&mut Vec<syn::Attribute>) -> Result<bool>,
6) -> Result {
7    VisitAttrs { visit }.visit_item(item)
8}
9
10struct VisitAttrs<T> {
11    visit: T,
12}
13
14impl<T> VisitAttrs<T>
15where
16    T: FnMut(&mut Vec<syn::Attribute>) -> Result<bool>,
17{
18    fn visit(&mut self, attrs: &mut Vec<syn::Attribute>) -> Result<bool> {
19        (self.visit)(attrs)
20    }
21
22    fn visit_item(&mut self, item: &mut syn::Item) -> Result {
23        match item {
24            syn::Item::Fn(fn_item) => self.visit_item_fn(fn_item),
25            syn::Item::Impl(impl_item) => self.visit_item_impl(impl_item),
26            _ => Ok(()),
27        }
28    }
29
30    fn visit_item_fn(&mut self, fn_item: &mut syn::ItemFn) -> Result {
31        if !self.visit(&mut fn_item.attrs)? {
32            bail!(
33                fn_item,
34                "This code should never be executed if there is a `#[cfg(...)]` attribute \
35                on the function itself, because if that cfg evaluates to `false`, \
36                no other proc macro on the item should be called."
37            )
38        }
39
40        self.visit_signature(&mut fn_item.sig)
41    }
42
43    fn visit_impl_item_fn(&mut self, fn_item: &mut syn::ImplItemFn) -> Result<bool> {
44        if !self.visit(&mut fn_item.attrs)? {
45            return Ok(false);
46        }
47
48        self.visit_signature(&mut fn_item.sig)?;
49
50        Ok(true)
51    }
52
53    fn visit_signature(&mut self, sig: &mut syn::Signature) -> Result {
54        sig.inputs.try_retain_mut(|arg| match arg {
55            syn::FnArg::Typed(arg) => self.visit(&mut arg.attrs),
56            syn::FnArg::Receiver(arg) => self.visit(&mut arg.attrs),
57        })?;
58
59        self.visit_generics(&mut sig.generics)?;
60
61        if let Some(variadic) = &mut sig.variadic {
62            if !self.visit(&mut variadic.attrs)? {
63                sig.variadic = None;
64            }
65        }
66
67        // Where clause doesn't support attributes yet.
68        // There is an issue in rust-lang/rust:
69        // https://github.com/rust-lang/rust/issues/115590
70
71        Ok(())
72    }
73
74    fn visit_generics(&mut self, generics: &mut syn::Generics) -> Result {
75        generics.params.try_retain_mut(|param| match param {
76            syn::GenericParam::Type(param) => self.visit(&mut param.attrs),
77            syn::GenericParam::Lifetime(param) => self.visit(&mut param.attrs),
78            syn::GenericParam::Const(param) => self.visit(&mut param.attrs),
79        })
80    }
81
82    fn visit_item_impl(&mut self, impl_item: &mut syn::ItemImpl) -> Result {
83        if !self.visit(&mut impl_item.attrs)? {
84            bail!(
85                impl_item,
86                "This code should never be executed if there is a `#[cfg(...)]` attribute \
87                on the impl itself, because if that cfg evaluates to `false`, \
88                no other proc macro on the item should be called."
89            )
90        }
91
92        self.visit_generics(&mut impl_item.generics)?;
93
94        impl_item.items.try_retain_mut(|item| match item {
95            syn::ImplItem::Fn(fn_item) => self.visit_impl_item_fn(fn_item),
96            _ => Ok(true),
97        })
98    }
99}