]> git.proxmox.com Git - rustc.git/blame - vendor/failure_derive/src/lib.rs
New upstream version 1.34.2+dfsg1
[rustc.git] / vendor / failure_derive / src / lib.rs
CommitLineData
8faf50e0 1extern crate proc_macro2;
94b46f34
XL
2extern crate syn;
3
8faf50e0
XL
4#[macro_use]
5extern crate synstructure;
6#[macro_use]
7extern crate quote;
8
9fa01778
XL
9use proc_macro2::{TokenStream, Span};
10use syn::LitStr;
11use syn::spanned::Spanned;
12
13#[derive(Debug)]
14struct Error(TokenStream);
15
16impl Error {
17 fn new(span: Span, message: &str) -> Error {
18 Error(quote_spanned! { span =>
19 compile_error!(#message);
20 })
21 }
22
23 fn into_tokens(self) -> TokenStream {
24 self.0
25 }
26}
94b46f34
XL
27
28decl_derive!([Fail, attributes(fail, cause)] => fail_derive);
29
8faf50e0 30fn fail_derive(s: synstructure::Structure) -> TokenStream {
9fa01778
XL
31 match fail_derive_impl(s) {
32 Err(err) => err.into_tokens(),
33 Ok(tokens) => tokens,
34 }
35}
36
37fn fail_derive_impl(s: synstructure::Structure) -> Result<TokenStream, Error> {
8faf50e0
XL
38 let make_dyn = if cfg!(has_dyn_trait) {
39 quote! { &dyn }
40 } else {
41 quote! { & }
42 };
43
9fa01778
XL
44 let ty_name = LitStr::new(&s.ast().ident.to_string(), Span::call_site());
45
94b46f34
XL
46 let cause_body = s.each_variant(|v| {
47 if let Some(cause) = v.bindings().iter().find(is_cause) {
0731742a 48 quote!(return Some(::failure::AsFail::as_fail(#cause)))
94b46f34
XL
49 } else {
50 quote!(return None)
51 }
52 });
53
54 let bt_body = s.each_variant(|v| {
55 if let Some(bi) = v.bindings().iter().find(is_backtrace) {
56 quote!(return Some(#bi))
57 } else {
58 quote!(return None)
59 }
60 });
61
8faf50e0
XL
62 let fail = s.unbound_impl(
63 quote!(::failure::Fail),
64 quote! {
9fa01778
XL
65 fn name(&self) -> Option<&str> {
66 Some(concat!(module_path!(), "::", #ty_name))
67 }
68
94b46f34 69 #[allow(unreachable_code)]
8faf50e0
XL
70 fn cause(&self) -> ::failure::_core::option::Option<#make_dyn(::failure::Fail)> {
71 match *self { #cause_body }
72 None
94b46f34 73 }
94b46f34 74
94b46f34 75 #[allow(unreachable_code)]
8faf50e0
XL
76 fn backtrace(&self) -> ::failure::_core::option::Option<&::failure::Backtrace> {
77 match *self { #bt_body }
78 None
94b46f34 79 }
8faf50e0
XL
80 },
81 );
9fa01778 82 let display = display_body(&s)?.map(|display_body| {
8faf50e0
XL
83 s.unbound_impl(
84 quote!(::failure::_core::fmt::Display),
85 quote! {
86 #[allow(unreachable_code)]
87 fn fmt(&self, f: &mut ::failure::_core::fmt::Formatter) -> ::failure::_core::fmt::Result {
88 match *self { #display_body }
89 write!(f, "An error has occurred.")
90 }
91 },
92 )
94b46f34
XL
93 });
94
9fa01778 95 Ok(quote! {
94b46f34
XL
96 #fail
97 #display
9fa01778 98 })
94b46f34
XL
99}
100
9fa01778 101fn display_body(s: &synstructure::Structure) -> Result<Option<quote::__rt::TokenStream>, Error> {
94b46f34 102 let mut msgs = s.variants().iter().map(|v| find_error_msg(&v.ast().attrs));
9fa01778
XL
103 if msgs.all(|msg| msg.map(|m| m.is_none()).unwrap_or(true)) {
104 return Ok(None);
8faf50e0 105 }
94b46f34 106
9fa01778
XL
107 let mut tokens = TokenStream::new();
108 for v in s.variants() {
8faf50e0 109 let msg =
9fa01778
XL
110 find_error_msg(&v.ast().attrs)?
111 .ok_or_else(|| Error::new(
112 v.ast().ident.span(),
113 "All variants must have display attribute."
114 ))?;
8faf50e0 115 if msg.nested.is_empty() {
9fa01778
XL
116 return Err(Error::new(
117 msg.span(),
118 "Expected at least one argument to fail attribute"
119 ));
94b46f34
XL
120 }
121
8faf50e0
XL
122 let format_string = match msg.nested[0] {
123 syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv)) if nv.ident == "display" => {
124 nv.lit.clone()
125 }
126 _ => {
9fa01778
XL
127 return Err(Error::new(
128 msg.span(),
129 "Fail attribute must begin `display = \"\"` to control the Display message."
130 ));
94b46f34 131 }
94b46f34 132 };
8faf50e0
XL
133 let args = msg.nested.iter().skip(1).map(|arg| match *arg {
134 syn::NestedMeta::Literal(syn::Lit::Int(ref i)) => {
135 let bi = &v.bindings()[i.value() as usize];
9fa01778 136 Ok(quote!(#bi))
94b46f34 137 }
8faf50e0
XL
138 syn::NestedMeta::Meta(syn::Meta::Word(ref id)) => {
139 let id_s = id.to_string();
140 if id_s.starts_with("_") {
141 if let Ok(idx) = id_s[1..].parse::<usize>() {
142 let bi = match v.bindings().get(idx) {
143 Some(bi) => bi,
144 None => {
9fa01778
XL
145 return Err(Error::new(
146 arg.span(),
147 &format!(
148 "display attempted to access field `{}` in `{}::{}` which \
8faf50e0 149 does not exist (there are {} field{})",
9fa01778
XL
150 idx,
151 s.ast().ident,
152 v.ast().ident,
153 v.bindings().len(),
154 if v.bindings().len() != 1 { "s" } else { "" }
155 )
156 ));
8faf50e0
XL
157 }
158 };
9fa01778 159 return Ok(quote!(#bi));
94b46f34
XL
160 }
161 }
162 for bi in v.bindings() {
163 if bi.ast().ident.as_ref() == Some(id) {
9fa01778 164 return Ok(quote!(#bi));
94b46f34
XL
165 }
166 }
9fa01778
XL
167 return Err(Error::new(
168 arg.span(),
169 &format!(
170 "Couldn't find field `{}` in `{}::{}`",
171 id,
172 s.ast().ident,
173 v.ast().ident
174 )
175 ));
94b46f34 176 }
9fa01778
XL
177 ref arg => {
178 return Err(Error::new(
179 arg.span(),
180 "Invalid argument to fail attribute!"
181 ));
182 },
94b46f34 183 });
9fa01778 184 let args = args.collect::<Result<Vec<_>, _>>()?;
94b46f34 185
9fa01778
XL
186 let pat = v.pat();
187 tokens.extend(quote!(#pat => { return write!(f, #format_string #(, #args)*) }));
188 }
189 Ok(Some(tokens))
94b46f34
XL
190}
191
9fa01778 192fn find_error_msg(attrs: &[syn::Attribute]) -> Result<Option<syn::MetaList>, Error> {
94b46f34
XL
193 let mut error_msg = None;
194 for attr in attrs {
8faf50e0
XL
195 if let Some(meta) = attr.interpret_meta() {
196 if meta.name() == "fail" {
197 if error_msg.is_some() {
9fa01778
XL
198 return Err(Error::new(
199 meta.span(),
200 "Cannot have two display attributes"
201 ));
94b46f34 202 } else {
8faf50e0
XL
203 if let syn::Meta::List(list) = meta {
204 error_msg = Some(list);
205 } else {
9fa01778
XL
206 return Err(Error::new(
207 meta.span(),
208 "fail attribute must take a list in parentheses"
209 ));
8faf50e0 210 }
94b46f34
XL
211 }
212 }
213 }
214 }
9fa01778 215 Ok(error_msg)
94b46f34
XL
216}
217
218fn is_backtrace(bi: &&synstructure::BindingInfo) -> bool {
8faf50e0
XL
219 match bi.ast().ty {
220 syn::Type::Path(syn::TypePath {
221 qself: None,
222 path: syn::Path {
223 segments: ref path, ..
224 },
225 }) => path.last().map_or(false, |s| {
226 s.value().ident == "Backtrace" && s.value().arguments.is_empty()
227 }),
228 _ => false,
229 }
94b46f34
XL
230}
231
232fn is_cause(bi: &&synstructure::BindingInfo) -> bool {
8faf50e0
XL
233 let mut found_cause = false;
234 for attr in &bi.ast().attrs {
235 if let Some(meta) = attr.interpret_meta() {
236 if meta.name() == "cause" {
237 if found_cause {
238 panic!("Cannot have two `cause` attributes");
239 }
240 found_cause = true;
241 }
242 if meta.name() == "fail" {
243 if let syn::Meta::List(ref list) = meta {
244 if let Some(ref pair) = list.nested.first() {
245 if let &&syn::NestedMeta::Meta(syn::Meta::Word(ref word)) = pair.value() {
246 if word == "cause" {
247 if found_cause {
248 panic!("Cannot have two `cause` attributes");
249 }
250 found_cause = true;
251 }
252 }
253 }
254 }
255 }
256 }
257 }
258 found_cause
94b46f34 259}