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 std::iter::FromIterator;
6
7 use syn::{
8 braced, bracketed, parenthesized, token, Attribute, Error, Ident, Index, LitInt, LitStr, Meta,
9 Result, Token,
10 };
11
12 pub struct Attrs<'a> {
13 pub display: Option<Display<'a>>,
14 pub source: Option<&'a Attribute>,
15 pub backtrace: Option<&'a Attribute>,
16 pub from: Option<&'a Attribute>,
17 pub transparent: Option<Transparent<'a>>,
18 }
19
20 #[derive(Clone)]
21 pub struct Display<'a> {
22 pub original: &'a Attribute,
23 pub fmt: LitStr,
24 pub args: TokenStream,
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
get(input: &[Attribute]) -> Result<Attrs>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
parse_error_attribute<'a>(attrs: &mut Attrs<'a>, attr: &'a Attribute) -> Result<()>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 display = Display {
109 original: attr,
110 fmt: input.parse()?,
111 args: parse_token_expr(input, false)?,
112 has_bonus_display: false,
113 implied_bounds: Set::new(),
114 };
115 if attrs.display.is_some() {
116 return Err(Error::new_spanned(
117 attr,
118 "only one #[error(...)] attribute is allowed",
119 ));
120 }
121 attrs.display = Some(display);
122 Ok(())
123 })
124 }
125
parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream>126 fn parse_token_expr(input: ParseStream, mut begin_expr: bool) -> Result<TokenStream> {
127 let mut tokens = Vec::new();
128 while !input.is_empty() {
129 if begin_expr && input.peek(Token![.]) {
130 if input.peek2(Ident) {
131 input.parse::<Token![.]>()?;
132 begin_expr = false;
133 continue;
134 }
135 if input.peek2(LitInt) {
136 input.parse::<Token![.]>()?;
137 let int: Index = input.parse()?;
138 let ident = format_ident!("_{}", int.index, span = int.span);
139 tokens.push(TokenTree::Ident(ident));
140 begin_expr = false;
141 continue;
142 }
143 }
144
145 begin_expr = input.peek(Token![break])
146 || input.peek(Token![continue])
147 || input.peek(Token![if])
148 || input.peek(Token![in])
149 || input.peek(Token![match])
150 || input.peek(Token![mut])
151 || input.peek(Token![return])
152 || input.peek(Token![while])
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 || input.peek(Token![*])
166 || input.peek(Token![-]);
167
168 let token: TokenTree = if input.peek(token::Paren) {
169 let content;
170 let delimiter = parenthesized!(content in input);
171 let nested = parse_token_expr(&content, true)?;
172 let mut group = Group::new(Delimiter::Parenthesis, nested);
173 group.set_span(delimiter.span.join());
174 TokenTree::Group(group)
175 } else if input.peek(token::Brace) {
176 let content;
177 let delimiter = braced!(content in input);
178 let nested = parse_token_expr(&content, true)?;
179 let mut group = Group::new(Delimiter::Brace, nested);
180 group.set_span(delimiter.span.join());
181 TokenTree::Group(group)
182 } else if input.peek(token::Bracket) {
183 let content;
184 let delimiter = bracketed!(content in input);
185 let nested = parse_token_expr(&content, true)?;
186 let mut group = Group::new(Delimiter::Bracket, nested);
187 group.set_span(delimiter.span.join());
188 TokenTree::Group(group)
189 } else {
190 input.parse()?
191 };
192 tokens.push(token);
193 }
194 Ok(TokenStream::from_iter(tokens))
195 }
196
197 impl ToTokens for Display<'_> {
to_tokens(&self, tokens: &mut TokenStream)198 fn to_tokens(&self, tokens: &mut TokenStream) {
199 let fmt = &self.fmt;
200 let args = &self.args;
201 tokens.extend(quote! {
202 ::core::write!(__formatter, #fmt #args)
203 });
204 }
205 }
206
207 impl ToTokens for Trait {
to_tokens(&self, tokens: &mut TokenStream)208 fn to_tokens(&self, tokens: &mut TokenStream) {
209 let trait_name = format_ident!("{}", format!("{:?}", self));
210 tokens.extend(quote!(::core::fmt::#trait_name));
211 }
212 }
213