thiserror_core_impl/
attr.rs

1use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
2use quote::{format_ident, quote, ToTokens};
3use std::collections::BTreeSet as Set;
4use syn::parse::ParseStream;
5use syn::{
6    braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Meta,
7    Result, Token,
8};
9
10pub struct Attrs<'a> {
11    pub display: Option<Display<'a>>,
12    pub source: Option<&'a Attribute>,
13    pub backtrace: Option<&'a Attribute>,
14    pub from: Option<&'a Attribute>,
15    pub transparent: Option<Transparent<'a>>,
16}
17
18#[derive(Clone)]
19pub struct Display<'a> {
20    pub original: &'a Attribute,
21    pub fmt: LitStr,
22    pub args: TokenStream,
23    pub has_bonus_display: bool,
24    pub implied_bounds: Set<(usize, Trait)>,
25}
26
27#[derive(Copy, Clone)]
28pub struct Transparent<'a> {
29    pub original: &'a Attribute,
30    pub span: Span,
31}
32
33#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
34pub enum Trait {
35    Debug,
36    Display,
37    Octal,
38    LowerHex,
39    UpperHex,
40    Pointer,
41    Binary,
42    LowerExp,
43    UpperExp,
44}
45
46pub fn get(input: &[Attribute]) -> Result<Attrs> {
47    let mut attrs = Attrs {
48        display: None,
49        source: None,
50        backtrace: None,
51        from: None,
52        transparent: None,
53    };
54
55    for attr in input {
56        if attr.path().is_ident("error") {
57            parse_error_attribute(&mut attrs, attr)?;
58        } else if attr.path().is_ident("source") {
59            attr.meta.require_path_only()?;
60            if attrs.source.is_some() {
61                return Err(Error::new_spanned(attr, "duplicate #[source] attribute"));
62            }
63            attrs.source = Some(attr);
64        } else if attr.path().is_ident("backtrace") {
65            attr.meta.require_path_only()?;
66            if attrs.backtrace.is_some() {
67                return Err(Error::new_spanned(attr, "duplicate #[backtrace] attribute"));
68            }
69            attrs.backtrace = Some(attr);
70        } else if attr.path().is_ident("from") {
71            match attr.meta {
72                Meta::Path(_) => {}
73                Meta::List(_) | Meta::NameValue(_) => {
74                    // Assume this is meant for derive_more crate or something.
75                    continue;
76                }
77            }
78            if attrs.from.is_some() {
79                return Err(Error::new_spanned(attr, "duplicate #[from] attribute"));
80            }
81            attrs.from = Some(attr);
82        }
83    }
84
85    Ok(attrs)
86}
87
88fn parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Result<()> {
89    syn::custom_keyword!(transparent);
90
91    attr.parse_args_with(|input: ParseStream| {
92        if let Some(kw) = input.parse::<Option<transparent>>()? {
93            if attrs.transparent.is_some() {
94                return Err(Error::new_spanned(
95                    attr,
96                    "duplicate #[error(transparent)] attribute",
97                ));
98            }
99            attrs.transparent = Some(Transparent {
100                original: attr,
101                span: kw.span,
102            });
103            return Ok(());
104        }
105
106        let display = Display {
107            original: attr,
108            fmt: input.parse()?,
109            args: parse_token_expr(input, false)?,
110            has_bonus_display: false,
111            implied_bounds: Set::new(),
112        };
113        if attrs.display.is_some() {
114            return Err(Error::new_spanned(
115                attr,
116                "only one #[error(...)] attribute is allowed",
117            ));
118        }
119        attrs.display = Some(display);
120        Ok(())
121    })
122}
123
124fn parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream> {
125    let mut tokens = Vec::new();
126    while !input.is_empty() {
127        if begin_expr && input.peek(Token![.]) {
128            if input.peek2(Ident) {
129                input.parse::<Token![.]>()?;
130                begin_expr = false;
131                continue;
132            }
133            if input.peek2(LitInt) {
134                input.parse::<Token![.]>()?;
135                let int: Index = input.parse()?;
136                let ident = format_ident!("_{}", int.index, span = int.span);
137                tokens.push(TokenTree::Ident(ident));
138                begin_expr = false;
139                continue;
140            }
141        }
142
143        begin_expr = input.peek(Token![break])
144            || input.peek(Token![continue])
145            || input.peek(Token![if])
146            || input.peek(Token![in])
147            || input.peek(Token![match])
148            || input.peek(Token![mut])
149            || input.peek(Token![return])
150            || input.peek(Token![while])
151            || input.peek(Token![+])
152            || input.peek(Token![&])
153            || input.peek(Token![!])
154            || input.peek(Token![^])
155            || input.peek(Token![,])
156            || input.peek(Token![/])
157            || input.peek(Token![=])
158            || input.peek(Token![>])
159            || input.peek(Token![<])
160            || input.peek(Token![|])
161            || input.peek(Token![%])
162            || input.peek(Token![;])
163            || input.peek(Token![*])
164            || input.peek(Token![-]);
165
166        let token: TokenTree = if input.peek(token::Paren) {
167            let content;
168            let delimiter = parenthesized!(content in input);
169            let nested = parse_token_expr(&content, true)?;
170            let mut group = Group::new(Delimiter::Parenthesis, nested);
171            group.set_span(delimiter.span.join());
172            TokenTree::Group(group)
173        } else if input.peek(token::Brace) {
174            let content;
175            let delimiter = braced!(content in input);
176            let nested = parse_token_expr(&content, true)?;
177            let mut group = Group::new(Delimiter::Brace, nested);
178            group.set_span(delimiter.span.join());
179            TokenTree::Group(group)
180        } else if input.peek(token::Bracket) {
181            let content;
182            let delimiter = bracketed!(content in input);
183            let nested = parse_token_expr(&content, true)?;
184            let mut group = Group::new(Delimiter::Bracket, nested);
185            group.set_span(delimiter.span.join());
186            TokenTree::Group(group)
187        } else {
188            input.parse()?
189        };
190        tokens.push(token);
191    }
192    Ok(TokenStream::from_iter(tokens))
193}
194
195impl ToTokens for Display<'_> {
196    fn to_tokens(&self, tokens: &mut TokenStream) {
197        let fmt = &self.fmt;
198        let args = &self.args;
199        tokens.extend(quote! {
200            ::core::write!(__formatter, #fmt #args)
201        });
202    }
203}
204
205impl ToTokens for Trait {
206    fn to_tokens(&self, tokens: &mut TokenStream) {
207        let trait_name = format_ident!("{}", format!("{:?}", self));
208        tokens.extend(quote!(::core::fmt::#trait_name));
209    }
210}