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::discouraged::Speculative; |
5 | use syn::parse::ParseStream; |
6 | use syn::{ |
7 | braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Meta, |
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 requires_fmt_machinery: bool, |
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 | |
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 | |
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 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 | |
140 | fn 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 | } |
149 | if input.peek2(LitInt) { |
150 | input.parse::<Token![.]>()?; |
151 | let int: Index = input.parse()?; |
152 | let ident = format_ident!("_ {}" , int.index, span = int.span); |
153 | tokens.push(TokenTree::Ident(ident)); |
154 | begin_expr = false; |
155 | continue; |
156 | } |
157 | } |
158 | |
159 | begin_expr = input.peek(Token![break]) |
160 | || input.peek(Token![continue]) |
161 | || input.peek(Token![if]) |
162 | || input.peek(Token![in]) |
163 | || input.peek(Token![match]) |
164 | || input.peek(Token![mut]) |
165 | || input.peek(Token![return]) |
166 | || input.peek(Token![while]) |
167 | || input.peek(Token![+]) |
168 | || input.peek(Token![&]) |
169 | || input.peek(Token![!]) |
170 | || input.peek(Token![^]) |
171 | || input.peek(Token![,]) |
172 | || input.peek(Token![/]) |
173 | || input.peek(Token![=]) |
174 | || input.peek(Token![>]) |
175 | || input.peek(Token![<]) |
176 | || input.peek(Token![|]) |
177 | || input.peek(Token![%]) |
178 | || input.peek(Token![;]) |
179 | || input.peek(Token![*]) |
180 | || input.peek(Token![-]); |
181 | |
182 | let token: TokenTree = if input.peek(token::Paren) { |
183 | let content; |
184 | let delimiter = parenthesized!(content in input); |
185 | let nested = parse_token_expr(&content, true)?; |
186 | let mut group = Group::new(Delimiter::Parenthesis, nested); |
187 | group.set_span(delimiter.span.join()); |
188 | TokenTree::Group(group) |
189 | } else if input.peek(token::Brace) { |
190 | let content; |
191 | let delimiter = braced!(content in input); |
192 | let nested = parse_token_expr(&content, true)?; |
193 | let mut group = Group::new(Delimiter::Brace, nested); |
194 | group.set_span(delimiter.span.join()); |
195 | TokenTree::Group(group) |
196 | } else if input.peek(token::Bracket) { |
197 | let content; |
198 | let delimiter = bracketed!(content in input); |
199 | let nested = parse_token_expr(&content, true)?; |
200 | let mut group = Group::new(Delimiter::Bracket, nested); |
201 | group.set_span(delimiter.span.join()); |
202 | TokenTree::Group(group) |
203 | } else { |
204 | input.parse()? |
205 | }; |
206 | tokens.push(token); |
207 | } |
208 | Ok(TokenStream::from_iter(tokens)) |
209 | } |
210 | |
211 | impl ToTokens for Display<'_> { |
212 | fn to_tokens(&self, tokens: &mut TokenStream) { |
213 | let fmt: &LitStr = &self.fmt; |
214 | let args: &TokenStream = &self.args; |
215 | |
216 | // Currently `write!(f, "text")` produces less efficient code than |
217 | // `f.write_str("text")`. We recognize the case when the format string |
218 | // has no braces and no interpolated values, and generate simpler code. |
219 | tokens.extend(iter:if self.requires_fmt_machinery { |
220 | quote! { |
221 | ::core::write!(__formatter, #fmt #args) |
222 | } |
223 | } else { |
224 | quote! { |
225 | __formatter.write_str(#fmt) |
226 | } |
227 | }); |
228 | } |
229 | } |
230 | |
231 | impl ToTokens for Trait { |
232 | fn to_tokens(&self, tokens: &mut TokenStream) { |
233 | let trait_name: Ident = format_ident!(" {}" , format!(" {:?}" , self)); |
234 | tokens.extend(iter:quote!(::core::fmt::#trait_name)); |
235 | } |
236 | } |
237 | |