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