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