abi_stable_derive/sabi_trait/
attribute_parsing.rs

1use super::{TraitDefinition, *};
2
3use as_derive_utils::parse_utils::ParseBufferExt;
4
5use syn::{parse::ParseBuffer, Attribute, ItemTrait, TraitItem, TraitItemMethod};
6
7#[allow(unused_imports)]
8use core_extensions::SelfOps;
9
10use crate::{arenas::Arenas, attribute_parsing::contains_doc_hidden, utils::LinearResult};
11
12/// Configuration parsed from the helper attributes of `#[sabi_trait]`
13pub(crate) struct SabiTraitOptions<'a> {
14    /// Whether the output of the proc-macro is printed with println.
15    pub(crate) debug_print_trait: bool,
16    pub(crate) debug_output_tokens: bool,
17    pub(crate) doc_hidden_attr: Option<&'a TokenStream2>,
18    pub(crate) trait_definition: TraitDefinition<'a>,
19}
20
21impl<'a> SabiTraitOptions<'a> {
22    fn new(
23        trait_: &'a ItemTrait,
24        this: SabiTraitAttrs<'a>,
25        arenas: &'a Arenas,
26        ctokens: &'a CommonTokens,
27    ) -> Result<Self, syn::Error> {
28        let doc_hidden_attr = if this.is_hidden {
29            Some(arenas.alloc(quote!(#[doc(hidden)])))
30        } else {
31            None
32        };
33
34        Ok(Self {
35            debug_print_trait: this.debug_print_trait,
36            debug_output_tokens: this.debug_output_tokens,
37            doc_hidden_attr,
38            trait_definition: TraitDefinition::new(trait_, this, arenas, ctokens)?,
39        })
40    }
41}
42
43mod kw {
44    syn::custom_keyword! {no_default_fallback}
45    syn::custom_keyword! {debug_print_trait}
46    syn::custom_keyword! {debug_output_tokens}
47    syn::custom_keyword! {use_dyntrait}
48    syn::custom_keyword! {use_dyn_trait}
49    syn::custom_keyword! {no_trait_impl}
50}
51
52////////////////////////////////////////////////////////////////////////////////
53
54/// The attributes used in the vtable,and the trait.
55#[derive(Debug, Clone, Default)]
56pub(crate) struct OwnedDeriveAndOtherAttrs {
57    /// The attributes used in the vtable.
58    pub(crate) derive_attrs: Vec<Attribute>,
59    /// The attributes used in the trait.
60    pub(crate) other_attrs: Vec<Attribute>,
61}
62
63////////////////////////////////////////////////////////////////////////////////
64
65/// The `syn` type for methods,as well as its attributes split by where they are used.
66#[derive(Debug, Clone)]
67pub(crate) struct MethodWithAttrs<'a> {
68    /// The attributes used in the vtable,and the trait.
69    pub(crate) attrs: OwnedDeriveAndOtherAttrs,
70    pub(crate) item: &'a TraitItemMethod,
71}
72
73impl<'a> MethodWithAttrs<'a> {
74    /// Constructs a `MethodWithAttrs` with no attributes.
75    fn new(item: &'a TraitItemMethod) -> Self {
76        Self {
77            attrs: OwnedDeriveAndOtherAttrs {
78                derive_attrs: Vec::new(),
79                other_attrs: Vec::new(),
80            },
81            item,
82        }
83    }
84}
85
86////////////////////////////////////////////////////////////////////////////////
87
88/// A datastructure used while parsing the helper attributes of `#[sabi_trait]`.
89#[derive(Default)]
90pub(super) struct SabiTraitAttrs<'a> {
91    /// Whether the output of the proc-macro is printed with println.
92    pub(super) debug_print_trait: bool,
93    /// The attributes used in the vtable,and the trait.
94    pub(super) attrs: OwnedDeriveAndOtherAttrs,
95    /// The `syn` type for methods,as well as their attributes split by where they are used.
96    pub(super) methods_with_attrs: Vec<MethodWithAttrs<'a>>,
97    /// Which type to use as the underlying implementation of the trait object,
98    /// either DynTrait or RObject.
99    pub(super) which_object: WhichObject,
100    /// If true,removes the `impl Trait for Trait_TO`
101    pub(super) disable_trait_impl: bool,
102    /// If true,doesn't use the default implementation of methods when
103    /// the vtable entry is absent.
104    pub(super) disable_inherent_default: Vec<bool>,
105
106    pub(super) is_hidden: bool,
107    pub(super) debug_output_tokens: bool,
108
109    pub(super) errors: LinearResult<()>,
110}
111
112/// Used as context while parsing helper attributes of `#[sabi_trait]`.
113#[derive(Debug, Copy, Clone)]
114enum ParseContext {
115    TraitAttr,
116    Method { index: usize },
117}
118
119/// Parses the helper attributes for `#[sabi_trait]`.
120pub(crate) fn parse_attrs_for_sabi_trait<'a>(
121    trait_: &'a ItemTrait,
122    arenas: &'a Arenas,
123    ctokens: &'a CommonTokens,
124) -> Result<SabiTraitOptions<'a>, syn::Error> {
125    let mut this = SabiTraitAttrs::default();
126
127    let assoc_fns: Vec<&'a TraitItemMethod> = trait_
128        .items
129        .iter()
130        .filter_map(|item| match item {
131            TraitItem::Method(x) => Some(x),
132            _ => None,
133        })
134        .collect();
135
136    this.methods_with_attrs.reserve(assoc_fns.len());
137
138    this.disable_inherent_default.resize(assoc_fns.len(), false);
139
140    parse_inner(&mut this, &*trait_.attrs, ParseContext::TraitAttr, arenas)?;
141
142    for (index, assoc_fn) in assoc_fns.iter().cloned().enumerate() {
143        this.methods_with_attrs.push(MethodWithAttrs::new(assoc_fn));
144
145        parse_inner(
146            &mut this,
147            &*assoc_fn.attrs,
148            ParseContext::Method { index },
149            arenas,
150        )?;
151    }
152
153    this.errors.take()?;
154
155    SabiTraitOptions::new(trait_, this, arenas, ctokens)
156}
157
158/// Parses all the attributes on an item.
159fn parse_inner<'a, I>(
160    this: &mut SabiTraitAttrs<'a>,
161    attrs: I,
162    pctx: ParseContext,
163    arenas: &'a Arenas,
164) -> Result<(), syn::Error>
165where
166    I: IntoIterator<Item = &'a Attribute>,
167{
168    for attr in attrs {
169        if attr.path.is_ident("sabi") {
170            attr.parse_args_with(|input: &ParseBuffer<'_>| {
171                parse_sabi_trait_attr(this, pctx, input, attr, arenas)
172            })?;
173        } else if attr.path.is_ident("doc")
174            && matches!(pctx, ParseContext::TraitAttr)
175            && syn::parse::Parser::parse2(contains_doc_hidden, attr.tokens.clone())?
176        {
177            this.is_hidden = true;
178        } else if let ParseContext::TraitAttr = pctx {
179            this.attrs.other_attrs.push(attr.clone());
180        } else if let ParseContext::Method { .. } = pctx {
181            this.methods_with_attrs
182                .last_mut()
183                .unwrap()
184                .attrs
185                .other_attrs
186                .push(attr.clone());
187        }
188    }
189    Ok(())
190}
191
192/// Parses the `#[sabi()]` attributes on an item.
193fn parse_sabi_trait_attr<'a>(
194    this: &mut SabiTraitAttrs<'a>,
195    pctx: ParseContext,
196    input: &ParseBuffer<'_>,
197    attr: &Attribute,
198    _arenas: &'a Arenas,
199) -> Result<(), syn::Error> {
200    fn push_attr(
201        this: &mut SabiTraitAttrs<'_>,
202        pctx: ParseContext,
203        input: &ParseBuffer<'_>,
204        attr: Attribute,
205    ) {
206        input.ignore_rest();
207        match pctx {
208            ParseContext::Method { .. } => {
209                this.methods_with_attrs
210                    .last_mut()
211                    .unwrap()
212                    .attrs
213                    .derive_attrs
214                    .push(attr);
215            }
216            ParseContext::TraitAttr => {
217                this.attrs.derive_attrs.push(attr);
218            }
219        }
220    }
221
222    if input.check_parse(kw::no_default_fallback)? {
223        match pctx {
224            ParseContext::TraitAttr => {
225                for is_disabled in &mut this.disable_inherent_default {
226                    *is_disabled = true;
227                }
228            }
229            ParseContext::Method { index } => {
230                this.disable_inherent_default[index] = true;
231            }
232        }
233    } else if input.check_parse(kw::debug_print_trait)? {
234        this.debug_print_trait = true;
235    } else if input.check_parse(kw::debug_output_tokens)? {
236        this.debug_output_tokens = true;
237    } else if let ParseContext::TraitAttr = pctx {
238        if input.check_parse(kw::use_dyntrait)? || input.check_parse(kw::use_dyn_trait)? {
239            this.which_object = WhichObject::DynTrait;
240        } else if input.check_parse(kw::no_trait_impl)? {
241            this.disable_trait_impl = true;
242        } else {
243            push_attr(this, pctx, input, attr.clone());
244        }
245    } else {
246        push_attr(this, pctx, input, attr.clone())
247    }
248    Ok(())
249}