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                but #[bon] attribute is absent on top of the impl block; this \
9                additional #[bon] attribute on the impl block is required for \
10                the macro to see the type of `Self` and properly generate \
11                the builder struct definition adjacently to the impl block.";
12
13            if let Some(receiver) = &self.fn_item.orig.sig.receiver() {
14                bail!(
15                    &receiver.self_token,
16                    "function contains a `self` parameter {explanation}"
17                );
18            }
19
20            let mut ctx = FindSelfReference::default();
21            ctx.visit_item_fn(&self.fn_item.orig);
22            if let Some(self_span) = ctx.self_span {
23                bail!(
24                    &self_span,
25                    "function contains a `Self` type reference {explanation}"
26                );
27            }
28        }
29
30        Ok(())
31    }
32
33    pub(crate) fn warnings(&self) -> TokenStream {
34        // We used to emit some warnings here previously, but then that logic
35        // was removed. The code for the `warnings()` method was preserved just
36        // in case if we need to issue some new warnings again. However, it's not
37        // critical. Feel free to eliminate this method if you feel like it.
38        let _ = self;
39
40        TokenStream::new()
41    }
42}
43
44#[derive(Default)]
45struct FindSelfReference {
46    self_span: Option<Span>,
47}
48
49impl Visit<'_> for FindSelfReference {
50    fn visit_item(&mut self, _: &syn::Item) {
51        // Don't recurse into nested items. We are interested in the reference
52        // to `Self` on the current item level
53    }
54
55    fn visit_path(&mut self, path: &syn::Path) {
56        if self.self_span.is_some() {
57            return;
58        }
59        syn::visit::visit_path(self, path);
60
61        let first_segment = match path.segments.first() {
62            Some(first_segment) => first_segment,
63            _ => return,
64        };
65
66        if first_segment.ident == "Self" {
67            self.self_span = Some(first_segment.ident.span());
68        }
69    }
70}