auto_impl/
attr.rs

1//! Internal attributes of the form `#[auto_impl(name(...))]` that can be
2//! attached to trait items.
3
4use proc_macro2::{Delimiter, TokenTree};
5use syn::{
6    spanned::Spanned,
7    visit_mut::{visit_item_trait_mut, VisitMut},
8    Attribute, Error, Meta, TraitItem,
9};
10
11use crate::proxy::{parse_types, ProxyType};
12
13/// Removes all `#[auto_impl]` attributes that are attached to methods of the
14/// given trait.
15pub(crate) fn remove_our_attrs(trait_def: &mut syn::ItemTrait) -> syn::Result<()> {
16    struct AttrRemover(syn::Result<()>);
17    impl VisitMut for AttrRemover {
18        fn visit_trait_item_mut(&mut self, item: &mut TraitItem) {
19            let item_span = item.span();
20            let (attrs, is_method) = match item {
21                TraitItem::Fn(m) => (&mut m.attrs, true),
22                TraitItem::Const(c) => (&mut c.attrs, false),
23                TraitItem::Type(t) => (&mut t.attrs, false),
24                TraitItem::Macro(m) => (&mut m.attrs, false),
25                _ => {
26                    let err = syn::Error::new(
27                        item.span(),
28                        "encountered unexpected `TraitItem`, cannot handle that, sorry!",
29                    );
30
31                    if let Err(ref mut current_err) = self.0 {
32                        current_err.combine(err);
33                    } else {
34                        self.0 = Err(err);
35                    };
36
37                    return;
38                }
39            };
40
41            // Make sure non-methods do not have our attributes.
42            if !is_method && attrs.iter().any(is_our_attr) {
43                let err = syn::Error::new(
44                    item_span,
45                    "`#[auto_impl]` attributes are only allowed on methods",
46                );
47
48                if let Err(ref mut current_err) = self.0 {
49                    current_err.combine(err);
50                } else {
51                    self.0 = Err(err);
52                };
53
54                return;
55            }
56
57            attrs.retain(|a| !is_our_attr(a));
58        }
59    }
60
61    let mut visitor = AttrRemover(Ok(()));
62    visit_item_trait_mut(&mut visitor, trait_def);
63
64    visitor.0
65}
66
67/// Checks if the given attribute is "our" attribute. That means that it's path
68/// is `auto_impl`.
69pub(crate) fn is_our_attr(attr: &Attribute) -> bool {
70    attr.path().is_ident("auto_impl")
71}
72
73/// Tries to parse the given attribute as one of our own `auto_impl`
74/// attributes. If it's invalid, an error is emitted and `Err(())` is returned.
75/// You have to make sure that `attr` is one of our attrs with `is_our_attr`
76/// before calling this function!
77pub(crate) fn parse_our_attr(attr: &Attribute) -> syn::Result<OurAttr> {
78    assert!(is_our_attr(attr));
79
80    // Get the body of the attribute (which has to be a ground, because we
81    // required the syntax `auto_impl(...)` and forbid stuff like
82    // `auto_impl = ...`).
83    let body = match &attr.meta {
84        Meta::List(list) => list.tokens.clone(),
85        _ => {
86            return Err(Error::new(
87                attr.span(),
88                "expected single group delimited by `()`",
89            ));
90        }
91    };
92
93    let mut it = body.clone().into_iter();
94
95    // Try to extract the name (we require the body to be `name(...)`).
96    let name = match it.next() {
97        Some(TokenTree::Ident(x)) => x,
98        Some(other) => {
99            return Err(Error::new(
100                other.span(),
101                format_args!("expected ident, found '{}'", other),
102            ));
103        }
104        None => {
105            return Err(Error::new(attr.span(), "expected ident, found nothing"));
106        }
107    };
108
109    // Extract the parameters (which again, have to be a group delimited by
110    // `()`)
111    let params = match it.next() {
112        Some(TokenTree::Group(ref g)) if g.delimiter() == Delimiter::Parenthesis => g.stream(),
113        Some(other) => {
114            return Err(Error::new(
115                other.span(),
116                format_args!(
117                    "expected arguments for '{}' in parenthesis `()`, found `{}`",
118                    name, other
119                ),
120            ));
121        }
122        None => {
123            return Err(Error::new(
124                body.span(),
125                format_args!(
126                    "expected arguments for '{}' in parenthesis `()`, found nothing",
127                    name,
128                ),
129            ));
130        }
131    };
132
133    // Finally match over the name of the attribute.
134    let out = if name == "keep_default_for" {
135        let proxy_types = parse_types(params.into());
136        OurAttr::KeepDefaultFor(proxy_types)
137    } else {
138        return Err(Error::new(
139            name.span(),
140            format_args!(
141                "invalid attribute '{}'; only `keep_default_for` is supported",
142                name
143            ),
144        ));
145    };
146
147    Ok(out)
148}
149
150/// Attributes of the form `#[auto_impl(...)]` that can be attached to items of
151/// the trait.
152#[derive(Clone, PartialEq, Debug)]
153pub(crate) enum OurAttr {
154    KeepDefaultFor(Vec<ProxyType>),
155}