bon_macros/builder/builder_gen/input_fn/
validation.rs

1use crate::util::prelude::*;
2use syn::visit::Visit;
3
4impl super::FnInputCtx<'_> {
5    pub(super) fn validate(&self) -> Result {
6        if self.impl_ctx.is_none() {
7            let explanation = "\
8                which likely means the builder attribute was used inside of an \
9                impl block; the impl block needs to be annotated with the #[bon] \
10                attribute and the builder attribute must be spelled as #[builder] \
11                without any additional path prefix, since it's used as a simple \
12                inert config attribute for #[bon] in impl blocks; more info on \
13                inert vs active attributes: \
14                https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes";
15
16            if let Some(receiver) = &self.fn_item.orig.sig.receiver() {
17                bail!(
18                    &receiver.self_token,
19                    "this function contains a `self` parameter {explanation}"
20                );
21            }
22
23            let mut ctx = FindSelfReference::default();
24            ctx.visit_item_fn(&self.fn_item.orig);
25            if let Some(self_span) = ctx.self_span {
26                bail!(
27                    &self_span,
28                    "this function contains a `Self` type reference {explanation}"
29                );
30            }
31        }
32
33        if let Some(const_) = &self.config.const_ {
34            if self.fn_item.orig.sig.constness.is_none() {
35                bail!(
36                    &const_,
37                    "#[builder(const)] requires the underlying function to be \
38                    marked as `const fn`"
39                );
40            }
41        }
42
43        Ok(())
44    }
45
46    pub(crate) fn warnings(&self) -> TokenStream {
47        // We used to emit some warnings here previously, but then that logic
48        // was removed. The code for the `warnings()` method was preserved just
49        // in case if we need to issue some new warnings again. However, it's not
50        // critical. Feel free to eliminate this method if you feel like it.
51        let _ = self;
52
53        TokenStream::new()
54    }
55}
56
57#[derive(Default)]
58struct FindSelfReference {
59    self_span: Option<Span>,
60}
61
62impl Visit<'_> for FindSelfReference {
63    fn visit_item(&mut self, _: &syn::Item) {
64        // Don't recurse into nested items. We are interested in the reference
65        // to `Self` on the current item level
66    }
67
68    fn visit_path(&mut self, path: &syn::Path) {
69        if self.self_span.is_some() {
70            return;
71        }
72        syn::visit::visit_path(self, path);
73
74        let first_segment = match path.segments.first() {
75            Some(first_segment) => first_segment,
76            _ => return,
77        };
78
79        if first_segment.ident == "Self" {
80            self.self_span = Some(first_segment.ident.span());
81        }
82    }
83}