1 use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
2 use quote::{format_ident, quote, ToTokens};
3 use std::collections::BTreeSet as Set;
4 use syn::parse::ParseStream;
5 use syn::{
6     braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Meta,
7     Result, Token,
8 };
9 
10 pub 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)]
19 pub 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)]
28 pub struct Transparent<'a> {
29     pub original: &'a Attribute,
30     pub span: Span,
31 }
32 
33 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
34 pub enum Trait {
35     Debug,
36     Display,
37     Octal,
38     LowerHex,
39     UpperHex,
40     Pointer,
41     Binary,
42     LowerExp,
43     UpperExp,
44 }
45 
get(input: &[Attribute]) -> Result<Attrs>46 pub 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 
parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Result<()>88 fn 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 
parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream>124 fn 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 
195 impl ToTokens for Display<'_> {
to_tokens(&self, tokens: &mut TokenStream)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 
205 impl ToTokens for Trait {
to_tokens(&self, tokens: &mut TokenStream)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 }
211