1use proc_macro2::{Delimiter, Group, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
2use quote::{format_ident, quote, ToTokens};
3use std::collections::BTreeSet as Set;
4use syn::parse::discouraged::Speculative;
5use syn::parse::ParseStream;
6use syn::{
7 braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitFloat, LitInt,
8 LitStr, Meta, Result, Token,
9};
10
11pub 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)]
20pub struct Display<'a> {
21 pub original: &'a Attribute,
22 pub fmt: LitStr,
23 pub args: TokenStream,
24 pub requires_fmt_machinery: bool,
25 pub has_bonus_display: bool,
26 pub implied_bounds: Set<(usize, Trait)>,
27}
28
29#[derive(Copy, Clone)]
30pub struct Transparent<'a> {
31 pub original: &'a Attribute,
32 pub span: Span,
33}
34
35#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
36pub enum Trait {
37 Debug,
38 Display,
39 Octal,
40 LowerHex,
41 UpperHex,
42 Pointer,
43 Binary,
44 LowerExp,
45 UpperExp,
46}
47
48pub 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 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
90fn 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 fmt: LitStr = input.parse()?;
109
110 let ahead = input.fork();
111 ahead.parse::<Option<Token![,]>>()?;
112 let args = if ahead.is_empty() {
113 input.advance_to(&ahead);
114 TokenStream::new()
115 } else {
116 parse_token_expr(input, false)?
117 };
118
119 let requires_fmt_machinery = !args.is_empty();
120
121 let display = Display {
122 original: attr,
123 fmt,
124 args,
125 requires_fmt_machinery,
126 has_bonus_display: false,
127 implied_bounds: Set::new(),
128 };
129 if attrs.display.is_some() {
130 return Err(Error::new_spanned(
131 attr,
132 "only one #[error(...)] attribute is allowed",
133 ));
134 }
135 attrs.display = Some(display);
136 Ok(())
137 })
138}
139
140fn parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream> {
141 let mut tokens = Vec::new();
142 while !input.is_empty() {
143 if begin_expr && input.peek(Token![.]) {
144 if input.peek2(Ident) {
145 input.parse::<Token![.]>()?;
146 begin_expr = false;
147 continue;
148 } else if input.peek2(LitInt) {
149 input.parse::<Token![.]>()?;
150 let int: Index = input.parse()?;
151 tokens.push({
152 let ident = format_ident!("_{}", int.index, span = int.span);
153 TokenTree::Ident(ident)
154 });
155 begin_expr = false;
156 continue;
157 } else if input.peek2(LitFloat) {
158 let ahead = input.fork();
159 ahead.parse::<Token![.]>()?;
160 let float: LitFloat = ahead.parse()?;
161 let repr = float.to_string();
162 let mut indices = repr.split('.').map(syn::parse_str::<Index>);
163 if let (Some(Ok(first)), Some(Ok(second)), None) =
164 (indices.next(), indices.next(), indices.next())
165 {
166 input.advance_to(&ahead);
167 tokens.push({
168 let ident = format_ident!("_{}", first, span = float.span());
169 TokenTree::Ident(ident)
170 });
171 tokens.push({
172 let mut punct = Punct::new('.', Spacing::Alone);
173 punct.set_span(float.span());
174 TokenTree::Punct(punct)
175 });
176 tokens.push({
177 let mut literal = Literal::u32_unsuffixed(second.index);
178 literal.set_span(float.span());
179 TokenTree::Literal(literal)
180 });
181 begin_expr = false;
182 continue;
183 }
184 }
185 }
186
187 begin_expr = input.peek(Token![break])
188 || input.peek(Token![continue])
189 || input.peek(Token![if])
190 || input.peek(Token![in])
191 || input.peek(Token![match])
192 || input.peek(Token![mut])
193 || input.peek(Token![return])
194 || input.peek(Token![while])
195 || input.peek(Token![+])
196 || input.peek(Token![&])
197 || input.peek(Token![!])
198 || input.peek(Token![^])
199 || input.peek(Token![,])
200 || input.peek(Token![/])
201 || input.peek(Token![=])
202 || input.peek(Token![>])
203 || input.peek(Token![<])
204 || input.peek(Token![|])
205 || input.peek(Token![%])
206 || input.peek(Token![;])
207 || input.peek(Token![*])
208 || input.peek(Token![-]);
209
210 let token: TokenTree = if input.peek(token::Paren) {
211 let content;
212 let delimiter = parenthesized!(content in input);
213 let nested = parse_token_expr(&content, true)?;
214 let mut group = Group::new(Delimiter::Parenthesis, nested);
215 group.set_span(delimiter.span.join());
216 TokenTree::Group(group)
217 } else if input.peek(token::Brace) {
218 let content;
219 let delimiter = braced!(content in input);
220 let nested = parse_token_expr(&content, true)?;
221 let mut group = Group::new(Delimiter::Brace, nested);
222 group.set_span(delimiter.span.join());
223 TokenTree::Group(group)
224 } else if input.peek(token::Bracket) {
225 let content;
226 let delimiter = bracketed!(content in input);
227 let nested = parse_token_expr(&content, true)?;
228 let mut group = Group::new(Delimiter::Bracket, nested);
229 group.set_span(delimiter.span.join());
230 TokenTree::Group(group)
231 } else {
232 input.parse()?
233 };
234 tokens.push(token);
235 }
236 Ok(TokenStream::from_iter(tokens))
237}
238
239impl ToTokens for Display<'_> {
240 fn to_tokens(&self, tokens: &mut TokenStream) {
241 let fmt = &self.fmt;
242 let args = &self.args;
243
244 tokens.extend(if self.requires_fmt_machinery {
248 quote! {
249 ::core::write!(__formatter, #fmt #args)
250 }
251 } else {
252 quote! {
253 __formatter.write_str(#fmt)
254 }
255 });
256 }
257}
258
259impl ToTokens for Trait {
260 fn to_tokens(&self, tokens: &mut TokenStream) {
261 let trait_name = match self {
262 Trait::Debug => "Debug",
263 Trait::Display => "Display",
264 Trait::Octal => "Octal",
265 Trait::LowerHex => "LowerHex",
266 Trait::UpperHex => "UpperHex",
267 Trait::Pointer => "Pointer",
268 Trait::Binary => "Binary",
269 Trait::LowerExp => "LowerExp",
270 Trait::UpperExp => "UpperExp",
271 };
272 let ident = Ident::new(trait_name, Span::call_site());
273 tokens.extend(quote!(::core::fmt::#ident));
274 }
275}