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