1use proc_macro_error::emit_error;
2use std::iter::Peekable;
34use crate::proc_macro::{token_stream, TokenStream, TokenTree};
56/// 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}
1819impl ProxyType {
20pub(crate) fn is_fn(&self) -> bool {
21matches!(*self, ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce)
22 }
23}
2425/// 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> {
35let mut out = Vec::new();
36let mut iter = args.into_iter().peekable();
3738// While there are still tokens left...
39while iter.peek().is_some() {
40// First, we expect one of the proxy types.
41if let Ok(ty) = eat_type(&mut iter) {
42 out.push(ty);
43 }
4445// 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).
48let comma_next =
49matches!(iter.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',');
5051if comma_next {
52let _ = iter.next();
53 }
54 }
5556 out
57}
5859/// 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]
63const 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 ";
68const EXPECTED_TEXT: &str = "expected '&' or ident.";
6970// We can unwrap because this function requires the iterator to be
71 // non-empty.
72let ty = match iter.next().unwrap() {
73 TokenTree::Group(group) => {
74emit_error!(
75 group.span(),
76"unexpected group, {}", EXPECTED_TEXT;
77 note = NOTE_TEXT;
78 );
7980return Err(());
81 }
8283 TokenTree::Literal(lit) => {
84emit_error!(
85 lit.span(),
86"unexpected literal, {}", EXPECTED_TEXT;
87 note = NOTE_TEXT;
88 );
8990return Err(());
91 }
9293 TokenTree::Punct(punct) => {
94// Only '&' are allowed. Everything else leads to an error.
95if punct.as_char() != '&' {
96emit_error!(
97 punct.span(),
98"unexpected punctuation '{}', {}", punct, EXPECTED_TEXT;
99 note = NOTE_TEXT;
100 );
101102return Err(());
103 }
104105// Check if the next token is `mut`. If not, we will ignore it.
106let is_mut_next =
107matches!(iter.peek(), Some(TokenTree::Ident(id)) if id.to_string() == "mut");
108109if is_mut_next {
110// Eat `mut`
111let _ = iter.next();
112 ProxyType::RefMut
113 } else {
114 ProxyType::Ref
115 }
116 }
117118 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_ => {
126emit_error!(
127 ident.span(),
128"unexpected '{}', {}", ident, EXPECTED_TEXT;
129 note = NOTE_TEXT;
130 );
131return Err(());
132 }
133 },
134 };
135136Ok(ty)
137}
138139// 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.