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