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 | |
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 | |
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 | |
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<'_> { |
196 | fn to_tokens(&self, tokens: &mut TokenStream) { |
197 | let fmt: &LitStr = &self.fmt; |
198 | let args: &TokenStream = &self.args; |
199 | tokens.extend(iter:quote! { |
200 | std::write!(__formatter, #fmt #args) |
201 | }); |
202 | } |
203 | } |
204 | |
205 | impl ToTokens for Trait { |
206 | fn to_tokens(&self, tokens: &mut TokenStream) { |
207 | let trait_name: Ident = format_ident!(" {}" , format!(" {:?}" , self)); |
208 | tokens.extend(iter:quote!(std::fmt::#trait_name)); |
209 | } |
210 | } |
211 | |