auto_impl/
proxy.rs

1use proc_macro_error::emit_error;
2use std::iter::Peekable;
3
4use crate::proc_macro::{token_stream, TokenStream, TokenTree};
5
6/// Types for which a trait can automatically be implemented.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub(crate) enum ProxyType {
9    Ref,
10    RefMut,
11    Arc,
12    Rc,
13    Box,
14    Fn,
15    FnMut,
16    FnOnce,
17}
18
19impl ProxyType {
20    pub(crate) fn is_fn(&self) -> bool {
21        matches!(*self, ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce)
22    }
23}
24
25/// Parses the attribute token stream into a list of proxy types.
26///
27/// The attribute token stream is the one in `#[auto_impl(...)]`. It is
28/// supposed to be a comma-separated list of possible proxy types. Legal values
29/// are `&`, `&mut`, `Box`, `Rc`, `Arc`, `Fn`, `FnMut` and `FnOnce`.
30///
31/// If the given TokenStream is not valid, errors are emitted as appropriate.
32/// Erroneous types will not be put into the Vec but rather simply skipped,
33/// the emitted errors will abort the compilation anyway.
34pub(crate) fn parse_types(args: TokenStream) -> Vec<ProxyType> {
35    let mut out = Vec::new();
36    let mut iter = args.into_iter().peekable();
37
38    // While there are still tokens left...
39    while iter.peek().is_some() {
40        // First, we expect one of the proxy types.
41        if let Ok(ty) = eat_type(&mut iter) {
42            out.push(ty);
43        }
44
45        // If the next token is a comma, we eat it (trailing commas are
46        // allowed). If not, nothing happens (in this case, it's probably the
47        // end of the stream, otherwise an error will occur later).
48        let comma_next =
49            matches!(iter.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',');
50
51        if comma_next {
52            let _ = iter.next();
53        }
54    }
55
56    out
57}
58
59/// Parses one `ProxyType` from the given token iterator. The iterator must not
60/// be empty!
61fn eat_type(iter: &mut Peekable<token_stream::IntoIter>) -> Result<ProxyType, ()> {
62    #[rustfmt::skip]
63    const NOTE_TEXT: &str = "\
64        attribute format should be `#[auto_impl(<types>)]` where `<types>` is \
65        a comma-separated list of types. Allowed values for types: `&`, \
66        `&mut`, `Box`, `Rc`, `Arc`, `Fn`, `FnMut` and `FnOnce`.\
67    ";
68    const EXPECTED_TEXT: &str = "expected '&' or ident.";
69
70    // We can unwrap because this function requires the iterator to be
71    // non-empty.
72    let ty = match iter.next().unwrap() {
73        TokenTree::Group(group) => {
74            emit_error!(
75                group.span(),
76                "unexpected group, {}", EXPECTED_TEXT;
77                note = NOTE_TEXT;
78            );
79
80            return Err(());
81        }
82
83        TokenTree::Literal(lit) => {
84            emit_error!(
85                lit.span(),
86                "unexpected literal, {}", EXPECTED_TEXT;
87                note = NOTE_TEXT;
88            );
89
90            return Err(());
91        }
92
93        TokenTree::Punct(punct) => {
94            // Only '&' are allowed. Everything else leads to an error.
95            if punct.as_char() != '&' {
96                emit_error!(
97                    punct.span(),
98                    "unexpected punctuation '{}', {}", punct, EXPECTED_TEXT;
99                    note = NOTE_TEXT;
100                );
101
102                return Err(());
103            }
104
105            // Check if the next token is `mut`. If not, we will ignore it.
106            let is_mut_next =
107                matches!(iter.peek(), Some(TokenTree::Ident(id)) if id.to_string() == "mut");
108
109            if is_mut_next {
110                // Eat `mut`
111                let _ = iter.next();
112                ProxyType::RefMut
113            } else {
114                ProxyType::Ref
115            }
116        }
117
118        TokenTree::Ident(ident) => match &*ident.to_string() {
119            "Box" => ProxyType::Box,
120            "Rc" => ProxyType::Rc,
121            "Arc" => ProxyType::Arc,
122            "Fn" => ProxyType::Fn,
123            "FnMut" => ProxyType::FnMut,
124            "FnOnce" => ProxyType::FnOnce,
125            _ => {
126                emit_error!(
127                    ident.span(),
128                    "unexpected '{}', {}", ident, EXPECTED_TEXT;
129                    note = NOTE_TEXT;
130                );
131                return Err(());
132            }
133        },
134    };
135
136    Ok(ty)
137}
138
139// Right now, we can't really write useful tests. Many functions from
140// `proc_macro` use a compiler internal session. This session is only valid
141// when we were actually called as a proc macro. We need to add tests once
142// this limitation of `proc_macro` is fixed.