1//! Internal attributes of the form `#[auto_impl(name(...))]` that can be
2//! attached to trait items.
34use 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};
1112use crate::proxy::{parse_types, ProxyType};
1314/// 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) {
17struct AttrRemover;
18impl VisitMut for AttrRemover {
19fn visit_trait_item_mut(&mut self, item: &mut TraitItem) {
20let item_span = item.span();
21let (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 };
3233// Make sure non-methods do not have our attributes.
34if !is_method && attrs.iter().any(is_our_attr) {
35emit_error!(
36 item_span,
37"`#[auto_impl]` attributes are only allowed on methods",
38 );
39 }
4041 attrs.retain(|a| !is_our_attr(a));
42 }
43 }
4445 visit_item_trait_mut(&mut AttrRemover, trait_def);
46}
4748/// 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}
5354/// 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, ()> {
59assert!(is_our_attr(attr));
6061// 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 = ...`).
64let tokens = attr.tokens.clone().into_iter().collect::<Vec<_>>();
65let body = match &*tokens {
66 [TokenTree::Group(g)] => g.stream(),
67_ => {
68emit_error!(
69 attr.tokens.span(),
70"expected single group delimited by `()`, found '{:?}'",
71 tokens,
72 );
73return Err(());
74 }
75 };
7677let mut it = body.clone().into_iter();
7879// Try to extract the name (we require the body to be `name(...)`).
80let name = match it.next() {
81Some(TokenTree::Ident(x)) => x,
82Some(other) => {
83emit_error!(other.span(), "expected ident, found '{}'", other);
84return Err(());
85 }
86None => {
87emit_error!(attr.tokens.span(), "expected ident, found nothing");
88return Err(());
89 }
90 };
9192// Extract the parameters (which again, have to be a group delimited by
93 // `()`)
94let params = match it.next() {
95Some(TokenTree::Group(ref g)) if g.delimiter() == Delimiter::Parenthesis => g.stream(),
96Some(other) => {
97emit_error!(
98 other.span(),
99"expected arguments for '{}' in parenthesis `()`, found `{}`",
100 name,
101 other,
102 );
103return Err(());
104 }
105None => {
106emit_error!(
107 body.span(),
108"expected arguments for '{}' in parenthesis `()`, found nothing",
109 name,
110 );
111return Err(());
112 }
113 };
114115// Finally match over the name of the attribute.
116let out = if name == "keep_default_for" {
117let proxy_types = parse_types(params.into());
118 OurAttr::KeepDefaultFor(proxy_types)
119 } else {
120emit_error!(
121 name.span(), "invalid attribute '{}'", name;
122 note = "only `keep_default_for` is supported";
123 );
124return Err(());
125 };
126127Ok(out)
128}
129130/// 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}