errore_impl/
attr.rs

1use proc_macro2::{Delimiter, Group, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
2use std::collections::BTreeSet as Set;
3
4use quote::{format_ident, quote, ToTokens};
5use syn::parse::discouraged::Speculative;
6use syn::parse::ParseStream;
7use syn::{
8    braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, Lifetime, LitFloat,
9    LitInt, LitStr, Meta, Result, Token,
10};
11
12#[derive(Default)]
13pub struct Attrs<'a> {
14    pub display: Option<Display<'a>>,
15    pub source: Option<&'a Attribute>,
16    pub from: Option<&'a Attribute>,
17    pub transparent: Option<Transparent<'a>>,
18    pub doc: Option<&'a Attribute>,
19}
20
21#[derive(Clone)]
22pub struct Display<'a> {
23    pub original: &'a Attribute,
24    pub fmt: LitStr,
25    pub args: TokenStream,
26    pub requires_fmt_machinery: bool,
27    pub has_bonus_display: bool,
28    pub implied_bounds: Set<(usize, Trait)>,
29    pub arg_tokens: Vec<Argument>,
30}
31
32impl<'a> Display<'a> {
33    pub fn recursing(&self) -> Option<TokenStream> {
34        for arg in &self.arg_tokens {
35            match arg {
36                // expressions with 'self' included (for e.g. in 'test_nested_display') doesn't work
37                // need to check whether a function/expression is used
38                // crate::attr::Argument::Ident(ident) => {
39                //     if ident == "self" {
40                //         return Some(
41                //             syn::Error::new_spanned(ident, "cannot return without recursing")
42                //                 .to_compile_error(),
43                //         );
44                //     }
45                // }
46                _ => {}
47            };
48        }
49        None
50    }
51}
52
53#[derive(Copy, Clone)]
54pub struct Transparent<'a> {
55    pub original: &'a Attribute,
56    pub span: Span,
57}
58
59#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
60pub enum Trait {
61    Debug,
62    Display,
63    Octal,
64    LowerHex,
65    UpperHex,
66    Pointer,
67    Binary,
68    LowerExp,
69    UpperExp,
70}
71
72#[allow(dead_code)]
73#[derive(Clone)]
74pub enum Argument {
75    Ident(Ident),
76    Lifetime(Lifetime),
77    Literal(Literal),
78    Punct(Punct),
79}
80
81pub fn get(input: &[Attribute]) -> Result<Attrs> {
82    let mut attrs = Attrs::default();
83
84    for attr in input {
85        if attr.path().is_ident("error") && attr.path().is_ident("display") {
86            return Err(Error::new_spanned(
87                attr,
88                "cannot specifiy #[error] and #[display] attribute at the same time",
89            ));
90        } else if attr.path().is_ident("error") {
91            parse_display_attribute(&mut attrs, attr, "error")?;
92        } else if attr.path().is_ident("display") {
93            parse_display_attribute(&mut attrs, attr, "display")?;
94        } else if attr.path().is_ident("source") {
95            attr.meta.require_path_only()?;
96            if attrs.source.is_some() {
97                return Err(Error::new_spanned(attr, "duplicate #[source] attribute"));
98            }
99            attrs.source = Some(attr);
100        } else if attr.path().is_ident("from") {
101            match attr.meta {
102                Meta::Path(_) => {}
103                Meta::List(_) | Meta::NameValue(_) => {
104                    // Assume this is meant for derive_more crate or something.
105                    continue;
106                }
107            }
108            if attrs.from.is_some() {
109                return Err(Error::new_spanned(attr, "duplicate #[from] attribute"));
110            }
111            attrs.from = Some(attr);
112        } else if attr.path().is_ident("doc") {
113            attrs.doc = Some(attr);
114        }
115    }
116
117    Ok(attrs)
118}
119
120fn parse_display_attribute<'a>(
121    attrs: &mut Attrs<'a>,
122    attr: &'a Attribute,
123    name: &'static str,
124) -> Result<()> {
125    syn::custom_keyword!(transparent);
126
127    attr.parse_args_with(|input: ParseStream| {
128        if let Some(kw) = input.parse::<Option<transparent>>()? {
129            if attrs.transparent.is_some() {
130                return Err(Error::new_spanned(
131                    attr,
132                    format!("duplicate #[{}(transparent)] attribute", name),
133                ));
134            }
135            attrs.transparent = Some(Transparent {
136                original: attr,
137                span: kw.span,
138            });
139            return Ok(());
140        }
141
142        let fmt: LitStr = input.parse()?;
143
144        let ahead = input.fork();
145        ahead.parse::<Option<Token![,]>>()?;
146        let mut arg_tokens = Vec::<Argument>::with_capacity(10);
147        let args = if ahead.is_empty() {
148            input.advance_to(&ahead);
149            TokenStream::new()
150        } else {
151            parse_token_expr(input, false, &mut arg_tokens)?
152        };
153
154        let requires_fmt_machinery = !args.is_empty();
155
156        let display = Display {
157            original: attr,
158            fmt,
159            args,
160            requires_fmt_machinery,
161            has_bonus_display: false,
162            implied_bounds: Set::new(),
163            arg_tokens,
164        };
165        if attrs.display.is_some() {
166            return Err(Error::new_spanned(
167                attr,
168                format!("only one #[{}(...)] attribute is allowed", name),
169            ));
170        }
171        attrs.display = Some(display);
172        Ok(())
173    })
174}
175
176fn parse_token_expr(
177    input: ParseStream,
178    mut begin_expr: bool,
179    arg_tokens: &mut Vec<Argument>,
180) -> Result<TokenStream> {
181    let mut tokens = Vec::new();
182    while !input.is_empty() {
183        if let Some((ident, _)) = input.cursor().ident() {
184            arg_tokens.push(Argument::Ident(ident));
185        } else if let Some((lifetime, _)) = input.cursor().lifetime() {
186            arg_tokens.push(Argument::Lifetime(lifetime));
187        } else if let Some((literal, _)) = input.cursor().literal() {
188            arg_tokens.push(Argument::Literal(literal));
189        } else if let Some((punct, _)) = input.cursor().punct() {
190            arg_tokens.push(Argument::Punct(punct));
191        }
192
193        if begin_expr && input.peek(Token![.]) {
194            if input.peek2(Ident) {
195                input.parse::<Token![.]>()?;
196                begin_expr = false;
197                continue;
198            } else if input.peek2(LitInt) {
199                input.parse::<Token![.]>()?;
200                let int: Index = input.parse()?;
201                tokens.push({
202                    let ident = format_ident!("_{}", int.index, span = int.span);
203                    TokenTree::Ident(ident)
204                });
205                begin_expr = false;
206                continue;
207            } else if input.peek2(LitFloat) {
208                let ahead = input.fork();
209                ahead.parse::<Token![.]>()?;
210                let float: LitFloat = ahead.parse()?;
211                let repr = float.to_string();
212                let mut indices = repr.split('.').map(syn::parse_str::<Index>);
213                if let (Some(Ok(first)), Some(Ok(second)), None) =
214                    (indices.next(), indices.next(), indices.next())
215                {
216                    input.advance_to(&ahead);
217                    tokens.push({
218                        let ident = format_ident!("_{}", first, span = float.span());
219                        TokenTree::Ident(ident)
220                    });
221                    tokens.push({
222                        let mut punct = Punct::new('.', Spacing::Alone);
223                        punct.set_span(float.span());
224                        TokenTree::Punct(punct)
225                    });
226                    tokens.push({
227                        let mut literal = Literal::u32_unsuffixed(second.index);
228                        literal.set_span(float.span());
229                        TokenTree::Literal(literal)
230                    });
231                    begin_expr = false;
232                    continue;
233                }
234            }
235        }
236
237        begin_expr = input.peek(Token![break])
238            || input.peek(Token![continue])
239            || input.peek(Token![if])
240            || input.peek(Token![in])
241            || input.peek(Token![match])
242            || input.peek(Token![mut])
243            || input.peek(Token![return])
244            || input.peek(Token![while])
245            || input.peek(Token![+])
246            || input.peek(Token![&])
247            || input.peek(Token![!])
248            || input.peek(Token![^])
249            || input.peek(Token![,])
250            || input.peek(Token![/])
251            || input.peek(Token![=])
252            || input.peek(Token![>])
253            || input.peek(Token![<])
254            || input.peek(Token![|])
255            || input.peek(Token![%])
256            || input.peek(Token![;])
257            || input.peek(Token![*])
258            || input.peek(Token![-]);
259
260        let token: TokenTree = if input.peek(token::Paren) {
261            let content;
262            let delimiter = parenthesized!(content in input);
263            let nested = parse_token_expr(&content, true, arg_tokens)?;
264            let mut group = Group::new(Delimiter::Parenthesis, nested);
265            group.set_span(delimiter.span.join());
266            TokenTree::Group(group)
267        } else if input.peek(token::Brace) {
268            let content;
269            let delimiter = braced!(content in input);
270            let nested = parse_token_expr(&content, true, arg_tokens)?;
271            let mut group = Group::new(Delimiter::Brace, nested);
272            group.set_span(delimiter.span.join());
273            TokenTree::Group(group)
274        } else if input.peek(token::Bracket) {
275            let content;
276            let delimiter = bracketed!(content in input);
277            let nested = parse_token_expr(&content, true, arg_tokens)?;
278            let mut group = Group::new(Delimiter::Bracket, nested);
279            group.set_span(delimiter.span.join());
280            TokenTree::Group(group)
281        } else {
282            input.parse()?
283        };
284        tokens.push(token);
285    }
286    Ok(TokenStream::from_iter(tokens))
287}
288
289impl ToTokens for Display<'_> {
290    fn to_tokens(&self, tokens: &mut TokenStream) {
291        let fmt = &self.fmt;
292        let args = &self.args;
293
294        // Currently `write!(f, "text")` produces less efficient code than
295        // `f.write_str("text")`. We recognize the case when the format string
296        // has no braces and no interpolated values, and generate simpler code.
297        tokens.extend(if self.requires_fmt_machinery {
298            quote! {
299                ::core::write!(__formatter, #fmt #args)
300            }
301        } else {
302            quote! {
303                __formatter.write_str(#fmt)
304            }
305        });
306    }
307}
308
309impl ToTokens for Trait {
310    fn to_tokens(&self, tokens: &mut TokenStream) {
311        let trait_name = match self {
312            Trait::Debug => "Debug",
313            Trait::Display => "Display",
314            Trait::Octal => "Octal",
315            Trait::LowerHex => "LowerHex",
316            Trait::UpperHex => "UpperHex",
317            Trait::Pointer => "Pointer",
318            Trait::Binary => "Binary",
319            Trait::LowerExp => "LowerExp",
320            Trait::UpperExp => "UpperExp",
321        };
322        let ident = Ident::new(trait_name, Span::call_site());
323        tokens.extend(quote!(::core::fmt::#ident));
324    }
325}