derive_error_chain/
lib.rs

1#![recursion_limit = "300"]
2
3#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
4#![cfg_attr(feature = "cargo-clippy", allow(
5	large_enum_variant,
6	too_many_arguments,
7	use_self,
8))]
9
10//! A Macros 1.1 implementation of <https://crates.io/crates/error-chain>
11//!
12//! The `error-chain` example
13//!
14//! ```
15//! # #[macro_use] extern crate error_chain;
16//! #
17//! mod other_error {
18//!     error_chain! {}
19//! }
20//!
21//! error_chain! {
22//!     types {
23//!         Error, ErrorKind, ResultExt, Result;
24//!     }
25//!
26//!     links {
27//!         Another(other_error::Error, other_error::ErrorKind) #[cfg(unix)];
28//!     }
29//!
30//!     foreign_links {
31//!         Fmt(::std::fmt::Error);
32//!         Io(::std::io::Error) #[cfg(unix)];
33//!     }
34//!
35//!     errors {
36//!         InvalidToolchainName(t: String) {
37//!             description("invalid toolchain name")
38//!             display("invalid toolchain name: '{}'", t)
39//!         }
40//!     }
41//! }
42//! ```
43//!
44//! becomes
45//!
46//! ```
47//! # #[macro_use] extern crate derive_error_chain;
48//! # #[macro_use] extern crate error_chain;
49//! #
50//! mod other_error {
51//!     #[derive(Debug, ErrorChain)]
52//!     pub enum ErrorKind {
53//!         Msg(String),
54//!     }
55//! }
56//!
57//! #[derive(Debug, ErrorChain)]
58//! pub enum ErrorKind {
59//!     Msg(String),
60//!
61//!     #[cfg(unix)]
62//!     #[error_chain(link = "other_error::Error")]
63//!     Another(other_error::ErrorKind),
64//!
65//!     #[error_chain(foreign)]
66//!     Fmt(::std::fmt::Error),
67//!
68//!     #[cfg(unix)]
69//!     #[error_chain(foreign)]
70//!     Io(::std::io::Error),
71//!
72//!     #[error_chain(custom)]
73//!     #[error_chain(description = r#"|_| "invalid toolchain name""#)]
74//!     #[error_chain(display = r#"|t| write!(f, "invalid toolchain name: '{}'", t)"#)]
75//!     InvalidToolchainName(String),
76//! }
77//! ```
78//!
79//! So the obvious differences from `error_chain!` are:
80//!
81//! - The `ErrorKind` is an enum instead of a macro invocation.
82//! - Error links are variants of the enum instead of lines inside the macro.
83//! - Links have explicit annotations marking them as chainable / foreign / custom instead of being grouped into corresponding sections of the macro.
84//! - Attributes like `#[cfg]` are applied to the variants directly instead of needing special syntax.
85//! - `description` and `display` are defined as function expressions specified as attribute values, instead of shorthands integrated into the macro syntax.
86//!
87//! The less obvious differences are:
88//!
89//! - The `ErrorKind` must explicitly implement `::std::fmt::Debug`, either automatically using `#[derive]` or manually implemented separately. `error_chain!` does this implicitly.
90//! - Unlike `error_chain!`, the `ErrorKind` need not have `pub` visibility. The generated `Error`, `Result` and `ResultExt` will have the same visibility as the `ErrorKind`.
91//! - The `ErrorKind` can have a special `Msg(String)` member for converting strings to the `ErrorKind`. `error_chain!` does this implicitly.
92//! - Unlike `error-chain`, the `Msg(String)` member is optional. If absent, the `ErrorKind` and `Error` will not impl `From<String>` and `From<&str>`.
93//! - Doc comments, since they're effectively attributes, can be applied on the enum variants without any special syntax like `error_chain!` has.
94//! - The `ErrorKind` can be generic.
95//!
96//! # Enum attributes
97//!
98//! - `#[error_chain(error = "ErrorName")]`
99//!
100//!     Override the name of the generated `Error` struct to the given name. If not provided, the struct will be named `Error`.
101//!
102//! - `#[error_chain(result_ext = "ResultExtName")]`
103//!
104//!     Override the name of the generated `ResultExt` trait to the given name. If not provided, the trait will be named `ResultExt`.
105//!
106//! - `#[error_chain(result = "ResultName")]`
107//!
108//!     Override the name of the generated `Result` type alias to the given name. If not provided, the alias will be named `Result`.
109//!     If set to the empty string `""`, the alias will not be generated at all.
110//!
111//! - `#[error_chain(backtrace = "false")]` or `#[error_chain(backtrace = false)]`
112//!
113//!     Disable backtrace functionality in the generated code. This should be kept in sync with the value of the `backtrace` feature of the `error-chain` crate.
114//!     In other words, if you set `backtrace = "false"` here, you must also specify `default-features = false` for `error-chain` in your `Cargo.toml`
115//!
116//! # Variant definitions
117//!
118//! - Chainable links
119//!
120//!     ```
121//!     # #[macro_use] extern crate derive_error_chain;
122//!     #
123//!     # mod other_error {
124//!     #     #[derive(Debug, ErrorChain)]
125//!     #     pub enum ErrorKind {
126//!     #         Msg(String),
127//!     #     }
128//!     # }
129//!     #
130//!     # #[derive(Debug, ErrorChain)]
131//!     # pub enum ErrorKind {
132//!     #[error_chain(link = "other_error::Error")]
133//!     Another(other_error::ErrorKind),
134//!     # }
135//!     ```
136//!
137//!     A chainable link is an error and errorkind that have been generated using `error-chain` or `derive-error-chain`. The variant must have a single field
138//!     to hold the chained errorkind, and the `link` attribute must specify a path to the chained error.
139//!
140//!     When the `proc_macro` feature is enabled, the value of the `link` attribute does not need to be stringified:
141//!
142//!     ```
143//!     # #![feature(proc_macro)]
144//!     #
145//!     # #[macro_use] extern crate derive_error_chain;
146//!     #
147//!     # mod other_error {
148//!     #     #[derive(Debug, ErrorChain)]
149//!     #     pub enum ErrorKind {
150//!     #         Msg(String),
151//!     #     }
152//!     # }
153//!     #
154//!     # #[derive(Debug, ErrorChain)]
155//!     # pub enum ErrorKind {
156//!     #[error_chain(link = other_error::Error)]
157//!     Another(other_error::ErrorKind),
158//!     # }
159//!     ```
160//!
161//! - Foreign links
162//!
163//!     ```
164//!     # #[macro_use] extern crate derive_error_chain;
165//!     #
166//!     # #[derive(Debug, ErrorChain)]
167//!     # pub enum ErrorKind {
168//!     #[error_chain(foreign)]
169//!     Fmt(::std::fmt::Error),
170//!     # }
171//!     ```
172//!
173//!     A foreign link is an error that implements `::std::error::Error` but otherwise does not follow `error-chain`'s conventions. The variant must have
174//!     a single field to hold the foreign error.
175//!
176//! - Custom links
177//!
178//!     ```
179//!     # #[macro_use] extern crate derive_error_chain;
180//!     #
181//!     # #[derive(Debug, ErrorChain)]
182//!     # pub enum ErrorKind {
183//!     #[error_chain(custom)]
184//!     InvalidToolchainName(String),
185//!     # }
186//!     ```
187//!
188//!     A custom link is an arbitrary variant that can hold any members.
189//!
190//! # Variant attributes
191//!
192//! In addition to the above attributes that identify the type of the variant's link, the below attributes can be used on all links.
193//!
194//! - `#[error_chain(description = "some_function_expression")]`
195//!
196//!     Specifies a function expression to be used to implement `ErrorKind::description()`.
197//!     This value is also returned from the implementation of `::std::error::Error::description()` on the generated `Error`.
198//!
199//!     This can be an inline lambda:
200//!
201//!     ```
202//!     # #[macro_use] extern crate derive_error_chain;
203//!     #
204//!     # #[derive(Debug, ErrorChain)]
205//!     # pub enum ErrorKind {
206//!         # #[error_chain(custom)]
207//!     #[error_chain(description = r#"|_| "invalid toolchain name""#)]
208//!     InvalidToolchainName(String),
209//!     # }
210//!     ```
211//!
212//!     or it can be a separate function:
213//!
214//!     ```
215//!     # #[macro_use] extern crate derive_error_chain;
216//!     #
217//!     # #[derive(Debug, ErrorChain)]
218//!     # pub enum ErrorKind {
219//!         # #[error_chain(custom)]
220//!     #[error_chain(description = "invalid_toolchain_name_error_description")]
221//!     InvalidToolchainName(String),
222//!     # }
223//!
224//!     // <snip>
225//!
226//!     fn invalid_toolchain_name_error_description(_: &str) -> &str {
227//!         "invalid toolchain name"
228//!     }
229//!     ```
230//!
231//!     The function expression must have the signature `(...) -> &'static str`. It should have one parameter for each field of the variant.
232//!     The fields are passed in by reference.
233//!
234//!     Thus in the above example, since `InvalidToolchainName` had a single field of type `String`, the function expression needed to be of type
235//!     `(&str) -> &'static str`
236//!
237//!     If not specified, the default implementation behaves in this way:
238//!
239//!     - Chainable links: Forwards to the chained error kind's `description()`
240//!     - Foreign links: Forwards to the foreign error's implementation of `::std::error::Error::description()`
241//!     - Custom links: Returns the stringified name of the variant.
242//!
243//!     When the `proc_macro` feature is enabled, the value does not need to be stringified:
244//!
245//!     ```
246//!     # #![feature(proc_macro)]
247//!     #
248//!     # #[macro_use] extern crate derive_error_chain;
249//!     #
250//!     # #[derive(Debug, ErrorChain)]
251//!     # pub enum ErrorKind {
252//!         # #[error_chain(custom)]
253//!     #[error_chain(description = |_| "invalid toolchain name")]
254//!     InvalidToolchainName(String),
255//!     # }
256//!     ```
257//!
258//!     ```
259//!     # #![feature(proc_macro)]
260//!     #
261//!     # #[macro_use] extern crate derive_error_chain;
262//!     #
263//!     # #[derive(Debug, ErrorChain)]
264//!     # pub enum ErrorKind {
265//!         # #[error_chain(custom)]
266//!     #[error_chain(description = invalid_toolchain_name_error_description)]
267//!     InvalidToolchainName(String),
268//!     # }
269//!     #
270//!     # fn invalid_toolchain_name_error_description(_: &str) -> &str {
271//!     #     "invalid toolchain name"
272//!     # }
273//!     ```
274//!
275//!     When the `proc_macro` feature is enabled, closure expressions that only call `write!` on the `::std::fmt::Formatter` can instead use a shorthand:
276//!
277//!     ```
278//!     # #![feature(proc_macro)]
279//!     #
280//!     # #[macro_use] extern crate derive_error_chain;
281//!     #
282//!     # #[derive(Debug, ErrorChain)]
283//!     # pub enum ErrorKind {
284//!         # #[error_chain(custom)]
285//!     #[error_chain(description = const("invalid toolchain name"))]
286//!     InvalidToolchainName(String),
287//!     # }
288//!     ```
289//!
290//! - `#[error_chain(display = "some_function_expression")]`
291//!
292//!     Specifies a function expression to be used to implement `::std::fmt::Display::fmt()` on the `ErrorKind` and generated `Error`
293//!
294//!     This can be an inline lambda:
295//!
296//!     ```
297//!     # #[macro_use] extern crate derive_error_chain;
298//!     #
299//!     # #[derive(Debug, ErrorChain)]
300//!     # pub enum ErrorKind {
301//!         # #[error_chain(custom)]
302//!     #[error_chain(display = r#"|t| write!(f, "invalid toolchain name: '{}'", t)"#)]
303//!     InvalidToolchainName(String),
304//!     # }
305//!     ```
306//!
307//!     or it can be a separate function:
308//!
309//!     ```
310//!     # #[macro_use] extern crate derive_error_chain;
311//!     #
312//!     # #[derive(Debug, ErrorChain)]
313//!     # pub enum ErrorKind {
314//!         # #[error_chain(custom)]
315//!     #[error_chain(display = "invalid_toolchain_name_error_display")]
316//!     InvalidToolchainName(String),
317//!     # }
318//!
319//!     // <snip>
320//!
321//!     fn invalid_toolchain_name_error_display(f: &mut ::std::fmt::Formatter, t: &str) -> ::std::fmt::Result {
322//!         write!(f, "invalid toolchain name: '{}'", t)
323//!     }
324//!     ```
325//!
326//!     The function expression must have the signature `(&mut ::std::fmt::Formatter, ...) -> ::std::fmt::Result`.
327//!     It should have one `&mut ::std::fmt::Formatter` parameter, and one parameter for each field of the variant. The fields are passed in by reference.
328//!     For brevity, closure expressions do not need the `&mut ::std::fmt::Formatter` parameter and instead capture `f` from the closure environment.
329//!
330//!     Thus in the above example, since `InvalidToolchainName` had a single field of type `String`, the function expression needed to be of type
331//!     `(&mut ::std::fmt::Formatter, &str) -> ::std::fmt::Result`
332//!
333//!     If not specified, the default implementation of `::std::fmt::Display::fmt()` behaves in this way:
334//!
335//!     - Chainable links: Forwards to the chained errorkind's implementation of `::std::fmt::Display::fmt()`
336//!     - Foreign links: Forwards to the foreign error's implementation of `::std::fmt::Display::fmt()`
337//!     - Custom links: Writes the description of the variant to the formatter.
338//!
339//!     When the `proc_macro` feature is enabled, the value does not need to be stringified:
340//!
341//!     ```
342//!     # #![feature(proc_macro)]
343//!     #
344//!     # #[macro_use] extern crate derive_error_chain;
345//!     #
346//!     # #[derive(Debug, ErrorChain)]
347//!     # pub enum ErrorKind {
348//!         # #[error_chain(custom)]
349//!     #[error_chain(display = |t| write!(f, "invalid toolchain name: '{}'", t))]
350//!     InvalidToolchainName(String),
351//!     # }
352//!     ```
353//!
354//!     ```
355//!     # #![feature(proc_macro)]
356//!     #
357//!     # #[macro_use] extern crate derive_error_chain;
358//!     #
359//!     # #[derive(Debug, ErrorChain)]
360//!     # pub enum ErrorKind {
361//!         # #[error_chain(custom)]
362//!     #[error_chain(display = invalid_toolchain_name_error_display)]
363//!     InvalidToolchainName(String),
364//!     # }
365//!     #
366//!     # fn invalid_toolchain_name_error_display(f: &mut ::std::fmt::Formatter, t: &str) -> ::std::fmt::Result {
367//!     #     write!(f, "invalid toolchain name: '{}'", t)
368//!     # }
369//!     ```
370//!
371//!     When the `proc_macro` feature is enabled, closure expressions that only call `write!` on the `::std::fmt::Formatter` can instead use a shorthand:
372//!
373//!     ```
374//!     # #![feature(proc_macro)]
375//!     #
376//!     # #[macro_use] extern crate derive_error_chain;
377//!     #
378//!     # #[derive(Debug, ErrorChain)]
379//!     # pub enum ErrorKind {
380//!     // Tuple variants use `{0}`, `{1}`, and so on
381//!         # #[error_chain(custom)]
382//!     #[error_chain(display = const("invalid toolchain name: '{0}'"))]
383//!     InvalidToolchainName(String),
384//!     # }
385//!     ```
386//!
387//!     ```
388//!     # #![feature(proc_macro)]
389//!     #
390//!     # #[macro_use] extern crate derive_error_chain;
391//!     #
392//!     # #[derive(Debug, ErrorChain)]
393//!     # pub enum ErrorKind {
394//!     // Struct variants use `{name_of_the_field}`
395//!         # #[error_chain(custom)]
396//!     #[error_chain(display = const("invalid toolchain name: '{name}'"))]
397//!     InvalidToolchainName { name: String },
398//!     # }
399//!     ```
400//!
401//! - `#[error_chain(cause = "some_function_expression")]`
402//!
403//!     Specifies a function expression to be used to implement `::std::fmt::Error::cause()` on the generated `Error`
404//!
405//!     This can be an inline lambda:
406//!
407//!     ```
408//!     # #[macro_use] extern crate derive_error_chain;
409//!     #
410//!     # #[derive(Debug, ErrorChain)]
411//!     # pub enum ErrorKind {
412//!         # #[error_chain(custom)]
413//!     #[error_chain(cause = "|_, err| err")]
414//!     Io(::std::path::PathBuf, ::std::io::Error),
415//!     # }
416//!     ```
417//!
418//!     or it can be a separate function:
419//!
420//!     ```
421//!     # #[macro_use] extern crate derive_error_chain;
422//!     #
423//!     # #[derive(Debug, ErrorChain)]
424//!     # pub enum ErrorKind {
425//!         # #[error_chain(custom)]
426//!     #[error_chain(cause = "parse_file_error_cause")]
427//!     Io(::std::path::PathBuf, ::std::io::Error),
428//!     # }
429//!
430//!     // <snip>
431//!
432//!     fn parse_file_error_cause<'a>(_: &::std::path::Path, err: &'a ::std::io::Error) -> &'a ::std::error::Error {
433//!         err
434//!     }
435//!     ```
436//!
437//!     The function expression must have the signature `(...) -> &::std::error::Error`. It should have one parameter for each field of the variant.
438//!     The fields are passed in by reference. The result is wrapped in `Option::Some()` for returning from `::std::error::Error::cause()`
439//!
440//!     Thus in the above example, since `Io` had two fields of type `::std::path::PathBuf` and `::std::io::Error`, the function expression needed to be of type
441//!     `(&::std::path::Path, &::std::io::Error) -> &::std::error::Error`
442//!
443//!     If not specified, the default implementation of `::std::error::Error::cause()` behaves in this way:
444//!
445//!     - Chainable links: Returns `None`
446//!     - Foreign links: Forwards to the foreign error's implementation of `::std::error::Error::cause()`
447//!     - Custom links: Returns `None`
448//!
449//!     When the `proc_macro` feature is enabled, the value does not need to be stringified:
450//!
451//!     ```
452//!     # #![feature(proc_macro)]
453//!     #
454//!     # #[macro_use] extern crate derive_error_chain;
455//!     #
456//!     # #[derive(Debug, ErrorChain)]
457//!     # pub enum ErrorKind {
458//!         # #[error_chain(custom)]
459//!     #[error_chain(cause = |_, err| err)]
460//!     Io(::std::path::PathBuf, ::std::io::Error),
461//!     # }
462//!     ```
463//!
464//!     ```
465//!     # #![feature(proc_macro)]
466//!     #
467//!     # #[macro_use] extern crate derive_error_chain;
468//!     #
469//!     # #[derive(Debug, ErrorChain)]
470//!     # pub enum ErrorKind {
471//!         # #[error_chain(custom)]
472//!     #[error_chain(cause = parse_file_error_cause)]
473//!     Io(::std::path::PathBuf, ::std::io::Error),
474//!     # }
475//!     #
476//!     # fn parse_file_error_cause<'a>(_: &::std::path::Path, err: &'a ::std::io::Error) -> &'a ::std::error::Error {
477//!     #     err
478//!     # }
479//!     ```
480//!
481//! # Conflicts with `error-chain` macros when the `proc_macro` feature is enabled
482//!
483//! If you have the `proc_macro` feature enabled and have code like this:
484//!
485//! ```compile_fail
486//! #![feature(proc_macro)]
487//!
488//! #[macro_use] extern crate derive_error_chain;
489//! #[macro_use] extern crate error_chain; // Want to use `bail!` and `quick_main!`
490//!
491//! #[derive(Debug, ErrorChain)]
492//! #[error_chain(result = "MyResult")]
493//! enum ErrorKind {
494//!     Msg(String),
495//! }
496//!
497//! quick_main!(|| -> MyResult<()> {
498//!     bail!("failed");
499//! });
500//! ```
501//!
502//! it'll fail to compile with:
503//!
504//! ```text,ignore
505//! error: macro `error_chain` may not be used in attributes
506//! ```
507//!
508//! This is because the compiler thinks `#[error_chain(result = "MyResult")]` is the invocation of an attribute macro, notices that `error_chain!` is
509//! a `macro_rules` macro brought into scope from the `error-chain` crate, and thus complains that a `macro_rules` macro cannot be used as
510//! an attribute macro. It does this even though there is no attribute macro named `error_chain` and that the custom derive from this crate
511//! has registered `error_chain` as an attribute it supports.
512//!
513//! See <https://github.com/rust-lang/rust/issues/38356#issuecomment-324277403> for the discussion.
514//!
515//! To work around this, don't use `#[macro_use]` with the `error-chain` crate. Instead, either `use` the macros you need from it:
516//!
517//! ```
518//! #![feature(proc_macro)]
519//!
520//! #[macro_use] extern crate derive_error_chain;
521//! extern crate error_chain;
522//!
523//! use error_chain::{ bail, quick_main };
524//!
525//! #[derive(Debug, ErrorChain)]
526//! #[error_chain(result = "MyResult")]
527//! enum ErrorKind {
528//!     Msg(String),
529//! }
530//!
531//! quick_main!(|| -> MyResult<()> {
532//!     bail!("failed");
533//! });
534//! ```
535//!
536//! or fully qualify their paths:
537//!
538//! ```
539//! #![feature(proc_macro)]
540//!
541//! #[macro_use] extern crate derive_error_chain;
542//! extern crate error_chain;
543//!
544//! #[derive(Debug, ErrorChain)]
545//! #[error_chain(result = "MyResult")]
546//! enum ErrorKind {
547//!     Msg(String),
548//! }
549//!
550//! error_chain::quick_main!(|| -> MyResult<()> {
551//!     error_chain::bail!("failed");
552//! });
553//! ```
554//!
555//! `use`ing the `error_chain!` macro itself is more complicated: it must be renamed so that it doesn't just cause the above error again,
556//! and other macros it uses must also be imported, even though they're an implementation detail:
557//!
558//! ```
559//! #![feature(proc_macro)]
560//!
561//! #[macro_use] extern crate derive_error_chain;
562//! extern crate error_chain;
563//!
564//! use error_chain::{ error_chain as error_chain_macro, error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace };
565//!
566//! #[derive(Debug, ErrorChain)]
567//! #[error_chain(error = "MyError", result = "MyResult", result_ext = "MyResultExt")]
568//! enum MyErrorKind {
569//!     Msg(String),
570//! }
571//!
572//! error_chain_macro! {
573//! }
574//! ```
575//!
576//! To use it fully-qualified, the macros it depends on must still be `use`d to bring them into scope:
577//!
578//! ```
579//! #![feature(proc_macro)]
580//!
581//! #[macro_use] extern crate derive_error_chain;
582//! extern crate error_chain;
583//!
584//! use error_chain::{ error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace };
585//!
586//! #[derive(Debug, ErrorChain)]
587//! #[error_chain(error = "MyError", result = "MyResult", result_ext = "MyResultExt")]
588//! enum MyErrorKind {
589//!     Msg(String),
590//! }
591//!
592//! error_chain::error_chain! {
593//! }
594//! ```
595//!
596//! It's possible this experience will be made better before the `proc_macro` feature stabilizes.
597
598extern crate proc_macro;
599extern crate proc_macro2;
600#[macro_use]
601extern crate quote;
602#[macro_use]
603extern crate syn;
604extern crate syntex_fmt_macros;
605
606#[proc_macro_derive(ErrorChain, attributes(error_chain))]
607pub fn derive_error_chain(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
608	let ast: syn::DeriveInput = syn::parse(input).unwrap();
609
610	let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
611
612	let mut generics_lifetime = ast.generics.clone();
613	generics_lifetime.params = std::iter::once(parse_quote!('__a)).chain(generics_lifetime.params).collect();
614	let (impl_generics_lifetime, _, _) = generics_lifetime.split_for_impl();
615
616	let mut result_generics = ast.generics.clone();
617	result_generics.params.push(parse_quote!(__T));
618	let (_, result_ty_generics, _) = result_generics.split_for_impl();
619
620	let mut result_ext_generics_t = ast.generics.clone();
621	result_ext_generics_t.params.push(parse_quote!(__T));
622	let (result_ext_impl_generics_t, result_ext_ty_generics_t, _) = result_ext_generics_t.split_for_impl();
623
624	let mut result_ext_generics_t_e = result_ext_generics_t.clone();
625	result_ext_generics_t_e.params.push(parse_quote!(__E: ::std::error::Error + ::std::marker::Send + 'static));
626	let (result_ext_impl_generics_t_e, _, _) = result_ext_generics_t_e.split_for_impl();
627
628	let generics: std::collections::HashSet<_> =
629		ast.generics.params.iter()
630		.filter_map(|param|
631			if let syn::GenericParam::Type(syn::TypeParam { ref ident, .. }) = *param {
632				Some(ident)
633			}
634			else {
635				None
636			})
637		.collect();
638
639	let TopLevelProperties {
640		error_kind_name,
641		error_kind_vis,
642		error_name,
643		result_ext_name,
644		result_name,
645		support_backtrace,
646		error_chain_name,
647	} = (&ast).into();
648
649	let result = match ast.data {
650		syn::Data::Enum(syn::DataEnum { variants, .. }) => {
651			let links: Vec<Link> = variants.into_iter().map(Into::into).collect();
652
653			let error_kind_description_cases = links.iter().map(|link| link.error_kind_description(&error_kind_name));
654
655			let error_kind_display_cases = links.iter().map(|link| link.error_kind_display_case(&error_kind_name));
656
657			let error_kind_from_impls =
658				links.iter().filter_map(|link|
659					link.error_kind_from_impl(
660						&error_kind_name,
661						&impl_generics, &impl_generics_lifetime, &ty_generics, where_clause,
662					));
663
664			let error_cause_cases = links.iter().filter_map(|link| link.error_cause_case(&error_kind_name));
665
666			let error_doc_comment = format!(r"The Error type.
667
668This struct is made of three things:
669
670- `{0}` which is used to determine the type of the error.
671- a backtrace, generated when the error is created.
672- an error chain, used for the implementation of `Error::cause()`.", error_kind_name);
673
674			let error_from_impls =
675				links.iter().filter_map(|link|
676					link.error_from_impl(
677						&error_kind_name, &error_name,
678						&generics,
679						&impl_generics, &impl_generics_lifetime, &ty_generics, where_clause,
680					));
681
682			let extract_backtrace_fn = if support_backtrace {
683				let chained_error_extract_backtrace_cases = links.iter().filter_map(Link::chained_error_extract_backtrace_case);
684
685				Some(quote! {
686					fn extract_backtrace(err: &(::std::error::Error + Send + 'static)) -> Option<::std::sync::Arc<#error_chain_name::Backtrace>> {
687						if let Some(err) = err.downcast_ref::<Self>() {
688							return err.1.backtrace.clone();
689						}
690
691						#(#chained_error_extract_backtrace_cases)*
692
693						None
694					}
695				})
696			}
697			else {
698				None
699			};
700
701			let result_ext_chain_err_doc_comment = format!("\
702				If the `Result` is an `Err` then `chain_err` evaluates the closure, \
703				which returns *some type that can be converted to `{}`*, \
704				boxes the original error to store as the cause, then returns a new error \
705				containing the original error.\
706			", error_kind_name);
707
708			let result_wrapper = result_name.map(|result_name| quote! {
709				/// Convenient wrapper around `::std::result::Result`
710				#error_kind_vis type #result_name #result_ty_generics = ::std::result::Result<__T, #error_name #ty_generics>;
711			});
712
713			quote! {
714				extern crate error_chain as #error_chain_name;
715
716				impl #impl_generics #error_kind_name #ty_generics #where_clause {
717					/// A string describing the error kind.
718					pub fn description(&self) -> &str {
719						#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
720						match *self {
721							#(#error_kind_description_cases)*
722						}
723					}
724				}
725
726				impl #impl_generics ::std::fmt::Display for #error_kind_name #ty_generics #where_clause {
727					fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
728						#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
729						match *self {
730							#(#error_kind_display_cases)*
731						}
732					}
733				}
734
735				#(#error_kind_from_impls)*
736
737				impl #impl_generics From<#error_name #ty_generics> for #error_kind_name #ty_generics #where_clause {
738					fn from(err: #error_name #ty_generics) -> Self { err.0 }
739				}
740
741				#[doc = #error_doc_comment]
742				#[derive(Debug)]
743				#error_kind_vis struct #error_name #impl_generics (
744					/// The kind of the error.
745					pub #error_kind_name #ty_generics,
746
747					/// Contains the error chain and the backtrace.
748					pub #error_chain_name::State,
749				) #where_clause ;
750
751				#[allow(unused)]
752				impl #impl_generics #error_name #ty_generics #where_clause {
753					/// Constructs an error from a kind, and generates a backtrace.
754					pub fn from_kind(kind: #error_kind_name #ty_generics) -> Self {
755						#error_name(kind, #error_chain_name::State::default())
756					}
757
758					/// Constructs a chained error from another error and a kind, and generates a backtrace.
759					pub fn with_chain<__E, __K>(error: __E, kind: __K) -> Self
760						where __E: ::std::error::Error + Send + 'static, __K: Into<#error_kind_name #ty_generics>
761					{
762						#error_name::with_boxed_chain(Box::new(error), kind)
763					}
764
765					/// Constructs a chained error from another boxed error and a kind, and generates a backtrace
766					pub fn with_boxed_chain<__K>(error: Box<::std::error::Error + Send>, kind: __K) -> #error_name #ty_generics
767						where __K: Into<#error_kind_name #ty_generics>
768					{
769						#error_name(kind.into(), #error_chain_name::State::new::<Self>(error))
770					}
771
772					/// Returns the kind of the error.
773					pub fn kind(&self) -> &#error_kind_name #ty_generics { &self.0 }
774
775					/// Iterates over the error chain.
776					pub fn iter(&self) -> #error_chain_name::Iter {
777						#error_chain_name::ChainedError::iter(self)
778					}
779
780					/// Returns the backtrace associated with this error.
781					pub fn backtrace(&self) -> Option<&#error_chain_name::Backtrace> {
782						self.1.backtrace()
783					}
784
785					/// Extends the error chain with a new entry.
786					pub fn chain_err<__F, __EK>(self, error: __F) -> Self where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> {
787						#error_name::with_chain(self, Self::from_kind(error().into()))
788					}
789				}
790
791				impl #impl_generics ::std::error::Error for #error_name #ty_generics #where_clause {
792					fn description(&self) -> &str { self.0.description() }
793
794					fn cause(&self) -> Option<&::std::error::Error> {
795						#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
796						match self.1.next_error {
797							Some(ref c) => Some(&**c),
798							None => match self.0 {
799								#(#error_cause_cases)*
800
801								_ => None,
802							},
803						}
804					}
805				}
806
807				impl #impl_generics ::std::fmt::Display for #error_name #ty_generics #where_clause {
808					fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
809						::std::fmt::Display::fmt(&self.0, f)
810					}
811				}
812
813				#(#error_from_impls)*
814
815				impl #impl_generics From<#error_kind_name #ty_generics> for #error_name #ty_generics #where_clause {
816					fn from(kind: #error_kind_name #ty_generics) -> Self { Self::from_kind(kind) }
817				}
818
819				impl #impl_generics ::std::ops::Deref for #error_name #ty_generics #where_clause {
820					type Target = #error_kind_name #ty_generics;
821
822					fn deref(&self) -> &Self::Target { &self.0 }
823				}
824
825				impl #impl_generics #error_chain_name::ChainedError for #error_name #ty_generics #where_clause {
826					type ErrorKind = #error_kind_name #ty_generics;
827
828					fn new(kind: Self::ErrorKind, state: #error_chain_name::State) -> Self {
829						#error_name(kind, state)
830					}
831
832					fn from_kind(kind: Self::ErrorKind) -> Self {
833						Self::from_kind(kind)
834					}
835
836					fn with_chain<__E, __K>(error: __E, kind: __K) -> Self
837						where __E: ::std::error::Error + Send + 'static, __K: Into<Self::ErrorKind> {
838
839						Self::with_chain(error, kind)
840					}
841
842					fn kind(&self) -> &Self::ErrorKind {
843						self.kind()
844					}
845
846					fn iter(&self) -> #error_chain_name::Iter {
847						#error_chain_name::Iter::new(Some(self))
848					}
849
850					fn backtrace(&self) -> Option<&#error_chain_name::Backtrace> {
851						self.backtrace()
852					}
853
854					fn chain_err<__F, __EK>(self, error: __F) -> Self where __F: FnOnce() -> __EK, __EK: Into<Self::ErrorKind> {
855						self.chain_err(error)
856					}
857
858					#extract_backtrace_fn
859				}
860
861				/// Additional methods for `Result` and `Option`, for easy interaction with this crate.
862				#error_kind_vis trait #result_ext_name #result_ext_impl_generics_t #where_clause {
863					#[doc = #result_ext_chain_err_doc_comment]
864					fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics>
865						where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics>;
866				}
867
868				impl #result_ext_impl_generics_t_e #result_ext_name #result_ext_ty_generics_t for ::std::result::Result<__T, __E> #where_clause {
869					fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics>
870						where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> {
871						self.map_err(move |e| {
872							let state = #error_chain_name::State::new::<#error_name #ty_generics>(Box::new(e));
873							#error_chain_name::ChainedError::new(callback().into(), state)
874						})
875					}
876				}
877
878				impl #result_ext_impl_generics_t #result_ext_name #result_ext_ty_generics_t for ::std::option::Option<__T> #where_clause {
879					fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics>
880						where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> {
881						self.ok_or_else(move || {
882							#error_chain_name::ChainedError::from_kind(callback().into())
883						})
884					}
885				}
886
887				#result_wrapper
888			}
889		},
890
891		_ => panic!("#[derive(ErrorChain] can only be used with enums."),
892	};
893
894	result.into()
895}
896
897struct TopLevelProperties {
898	error_kind_name: proc_macro2::Ident,
899	error_kind_vis: syn::Visibility,
900	error_name: proc_macro2::Ident,
901	result_ext_name: proc_macro2::Ident,
902	result_name: Option<proc_macro2::Ident>,
903	error_chain_name: proc_macro2::Ident,
904	support_backtrace: bool,
905}
906
907impl<'a> From<&'a syn::DeriveInput> for TopLevelProperties {
908	fn from(ast: &'a syn::DeriveInput) -> Self {
909		let mut error_name = proc_macro2::Ident::new("Error", proc_macro2::Span::call_site());
910		let mut result_ext_name = proc_macro2::Ident::new("ResultExt", proc_macro2::Span::call_site());
911		let mut result_name = Some(proc_macro2::Ident::new("Result", proc_macro2::Span::call_site()));
912		let mut support_backtrace = true;
913
914		for attr in &ast.attrs {
915			if !is_error_chain_attribute(attr) {
916				continue;
917			}
918
919			match attr.interpret_meta() {
920				Some(syn::Meta::List(syn::MetaList { nested, .. })) => {
921					for nested_meta in nested {
922						match nested_meta {
923							syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { ident, lit: syn::Lit::Str(value), .. })) => {
924								let value = &value.value();
925
926								match &*ident.to_string() {
927									"error" => error_name = syn::parse_str(value).unwrap_or_else(|err|
928										panic!("Could not parse `error` value as an identifier - {}", err)),
929
930									"result_ext" => result_ext_name = syn::parse_str(value).unwrap_or_else(|err|
931										panic!("Could not parse `result_ext` value as an identifier - {}", err)),
932
933									"result" => result_name =
934										if value == "" {
935											None
936										}
937										else {
938											Some(syn::parse_str(value).unwrap_or_else(|err|
939												panic!("Could not parse `result` value as an identifier - {}", err)))
940										},
941
942									"backtrace" => support_backtrace = value.parse().unwrap_or_else(|err|
943										panic!("Could not parse `backtrace` value - {}", err)),
944
945									_ =>
946										panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace` but got {}", ident),
947								}
948							},
949
950							syn::NestedMeta::Meta(syn::Meta::NameValue(
951								syn::MetaNameValue { ref ident, lit: syn::Lit::Bool(syn::LitBool { value, .. }), .. }))
952								if ident == "backtrace" => support_backtrace = value,
953
954							_ => panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace`"),
955						}
956					}
957				},
958
959				_ => panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace`"),
960			}
961		}
962
963		let error_chain_name = syn::parse_str(&format!("{}_error_chain", error_name)).unwrap_or_else(|err|
964			panic!("Could not generate error_chain crate name as a valid ident - {}", err));
965
966		TopLevelProperties {
967			error_kind_name: ast.ident.clone(),
968			error_kind_vis: ast.vis.clone(),
969			error_name,
970			result_ext_name,
971			result_name,
972			error_chain_name,
973			support_backtrace,
974		}
975	}
976}
977
978struct Link {
979	variant_ident: proc_macro2::Ident,
980	variant_fields: syn::Fields,
981	link_type: LinkType,
982	custom_description: Option<CustomFormatter>,
983	custom_display: Option<CustomFormatter>,
984	custom_cause: Option<syn::Expr>,
985}
986
987enum LinkType {
988	Msg,
989	Chainable(syn::Type, syn::Type),
990	Foreign(syn::Type),
991	Custom,
992}
993
994impl From<syn::Variant> for Link {
995	fn from(syn::Variant { ident: variant_ident, attrs, fields: variant_fields, .. }: syn::Variant) -> Self {
996		let is_msg = loop {
997			if variant_ident != "Msg" {
998				break false;
999			}
1000
1001			if let syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) = variant_fields {
1002				if unnamed.len() == 1 {
1003					if let syn::Type::Path(syn::TypePath { ref path, .. }) = unnamed[0].ty {
1004						if !path.global() && path.segments.len() == 1 && path.segments[0].ident == "String" {
1005							break true;
1006						}
1007					}
1008				}
1009			}
1010
1011			panic!("Expected Msg member to be a tuple of String");
1012		};
1013
1014		if is_msg {
1015			return Link {
1016				variant_ident,
1017				variant_fields,
1018				link_type: LinkType::Msg,
1019				custom_description: None,
1020				custom_display: None,
1021				custom_cause: None,
1022			};
1023		}
1024
1025		let mut link_type = None;
1026		let mut custom_description = None;
1027		let mut custom_display = None;
1028		let mut custom_cause: Option<syn::Expr> = None;
1029
1030		for attr in attrs {
1031			if !is_error_chain_attribute(&attr) {
1032				continue;
1033			}
1034
1035			if let Some(syn::Meta::List(syn::MetaList { nested, .. })) = attr.interpret_meta() {
1036				for nested_meta in nested {
1037					match nested_meta {
1038						syn::NestedMeta::Meta(syn::Meta::Word(ident)) => match &*ident.to_string() {
1039							"foreign" => match variant_fields {
1040								syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 =>
1041									link_type = Some(LinkType::Foreign(unnamed[0].ty.clone())),
1042
1043								_ => panic!("Foreign link {} must be a tuple of one element (the foreign error type).", variant_ident),
1044							},
1045
1046							"custom" => link_type = Some(LinkType::Custom),
1047
1048							_ => panic!(
1049								"Could not parse `error_chain` attribute of member {} - expected one of `foreign`, `custom` but got {}",
1050								variant_ident, ident),
1051						},
1052
1053						syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { ident, lit: syn::Lit::Str(value), .. })) => {
1054							let value = &value.value();
1055
1056							match &*ident.to_string() {
1057								"link" => match variant_fields {
1058									syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 =>
1059										link_type = Some(LinkType::Chainable(
1060											syn::parse_str(value).unwrap_or_else(|err|
1061												panic!("Could not parse `link` attribute of member {} as a type - {}", variant_ident, err)),
1062											unnamed[0].ty.clone())),
1063
1064									_ => panic!("Chainable link {} must be a tuple of one element (the chainable error kind).", variant_ident),
1065								},
1066
1067								"description" => custom_description = Some(CustomFormatter::Expr(syn::parse_str(value).unwrap_or_else(|err|
1068									panic!("Could not parse `description` attribute of member {} as an expression - {}", variant_ident, err)))),
1069
1070								"display" => custom_display = Some(CustomFormatter::Expr(syn::parse_str(value).unwrap_or_else(|err|
1071									panic!("Could not parse `display` attribute of member {} as an expression - {}", variant_ident, err)))),
1072
1073								"cause" => custom_cause = Some(syn::parse_str(value).unwrap_or_else(|err|
1074									panic!("Could not parse `cause` attribute of member {} as an expression - {}", variant_ident, err))),
1075
1076								_ => panic!(
1077									"Could not parse `error_chain` attribute of member {} - expected one of `link`, `description`, `display`, `cause` but got {}",
1078									variant_ident, ident),
1079							}
1080						},
1081
1082						_ => panic!("Could not parse `error_chain` attribute of member {} - expected term or name-value meta item", variant_ident),
1083					}
1084				}
1085			}
1086			else {
1087				let mut tts = {
1088					let mut tts = attr.tts.into_iter();
1089
1090					let tt = match tts.next() {
1091						Some(proc_macro2::TokenTree::Group(ref group)) if group.delimiter() == proc_macro2::Delimiter::Parenthesis => group.stream(),
1092						Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected `(tokens)` but found {}", variant_ident, tt),
1093						None => panic!("Could not parse `error_chain` attribute of member {} - expected `(tokens)`", variant_ident),
1094					};
1095
1096					if let Some(tt) = tts.next() {
1097						panic!("Could not parse `error_chain` attribute of member {} - unexpected token {} after `(tokens)`", variant_ident, tt);
1098					}
1099
1100					tt.into_iter()
1101				};
1102
1103				let ident = match tts.next() {
1104					Some(proc_macro2::TokenTree::Ident(ident)) => ident,
1105					Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected a term but got {}", variant_ident, tt),
1106					None => panic!("Could not parse `error_chain` attribute of member {} - expected a term", variant_ident),
1107				};
1108				let ident = ident.to_string();
1109
1110				match tts.next() {
1111					Some(proc_macro2::TokenTree::Punct(ref punct)) if punct.as_char() == '=' => (),
1112					Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected `=` but got {}", variant_ident, tt),
1113					None => panic!("Could not parse `error_chain` attribute of member {} - expected `=`", variant_ident),
1114				}
1115
1116				let value: proc_macro2::TokenStream = tts.collect();
1117				if value.is_empty() {
1118					panic!("Could not parse `error_chain` attribute of member {} - expected tokens after `=`", variant_ident);
1119				}
1120
1121				match &*ident {
1122					"link" => match variant_fields {
1123						syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 =>
1124							link_type = Some(LinkType::Chainable(
1125								syn::parse2(value).unwrap_or_else(|err|
1126									panic!("Could not parse `link` attribute of member {} as a type - {}", variant_ident, err)),
1127								unnamed[0].ty.clone())),
1128
1129						_ => panic!("Chainable link {} must be a tuple of one element (the chainable error kind).", variant_ident),
1130					},
1131
1132					"description" => custom_description = Some(CustomFormatter::parse(value, "description", &variant_ident, &variant_fields)),
1133
1134					"display" => custom_display = Some(CustomFormatter::parse(value, "display", &variant_ident, &variant_fields)),
1135
1136					"cause" => custom_cause = Some(syn::parse2(value).unwrap_or_else(|err|
1137						panic!("Could not parse `cause` attribute of member {} as an expression - {}", variant_ident, err))),
1138
1139					_ => panic!(
1140						"Could not parse `error_chain` attribute of member {} - expected one of `link`, `description`, `display`, `cause` but got {}",
1141						variant_ident, ident),
1142				}
1143			}
1144		}
1145
1146		let link_type = link_type.unwrap_or_else(||
1147			panic!(r#"Member {} does not have any of #[error_chain(link = "...")] or #[error_chain(foreign)] or #[error_chain(custom)]."#, variant_ident));
1148
1149		Link {
1150			variant_ident,
1151			variant_fields,
1152			link_type,
1153			custom_description,
1154			custom_display,
1155			custom_cause,
1156		}
1157	}
1158}
1159
1160impl Link {
1161	fn error_kind_description(&self, error_kind_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {
1162		let variant_ident = &self.variant_ident;
1163
1164		match (self.custom_description.as_ref(), &self.link_type) {
1165			(_, &LinkType::Msg) => quote! {
1166				#error_kind_name::#variant_ident(ref s) => s,
1167			},
1168
1169			(Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Chainable(_, _)) |
1170			(Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Foreign(_)) => quote! {
1171				#error_kind_name::#variant_ident(_) => #format_string,
1172			},
1173
1174			(Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Chainable(_, _)) |
1175			(Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Foreign(_)) if is_closure(custom_description) => quote! {
1176				#error_kind_name::#variant_ident(ref err) => {
1177					#[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1178					{ (#custom_description)(err) }
1179				},
1180			},
1181
1182			(Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Chainable(_, _)) |
1183			(Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Foreign(_)) => quote! {
1184				#error_kind_name::#variant_ident(ref err) => #custom_description(err),
1185			},
1186
1187			(Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Custom) => {
1188				let pattern = fields_pattern_ignore(&self.variant_fields);
1189
1190				quote! {
1191					#error_kind_name::#variant_ident #pattern => #format_string,
1192				}
1193			},
1194
1195			(Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Custom) => {
1196				let pattern = fields_pattern(&self.variant_fields);
1197				let args = args(&self.variant_fields);
1198
1199				if is_closure(custom_description) {
1200					quote! {
1201						#error_kind_name::#variant_ident #pattern => {
1202							#[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1203							{ (#custom_description)(#args) }
1204						},
1205					}
1206				}
1207				else {
1208					quote! {
1209						#error_kind_name::#variant_ident #pattern => #custom_description(#args),
1210					}
1211				}
1212			},
1213
1214			(None, &LinkType::Chainable(_, _)) => quote! {
1215				#error_kind_name::#variant_ident(ref kind) => kind.description(),
1216			},
1217
1218			(None, &LinkType::Foreign(_)) => quote! {
1219				#error_kind_name::#variant_ident(ref err) => ::std::error::Error::description(err),
1220			},
1221
1222			(None, &LinkType::Custom) => {
1223				let pattern = fields_pattern_ignore(&self.variant_fields);
1224
1225				quote! {
1226					#error_kind_name::#variant_ident #pattern => stringify!(#variant_ident),
1227				}
1228			},
1229		}
1230	}
1231
1232	fn error_kind_display_case(
1233		&self,
1234		error_kind_name: &proc_macro2::Ident,
1235	) -> proc_macro2::TokenStream {
1236		let variant_ident = &self.variant_ident;
1237
1238		match (self.custom_display.as_ref(), &self.link_type) {
1239			(_, &LinkType::Msg) => quote! {
1240				#error_kind_name::#variant_ident(ref s) => ::std::fmt::Display::fmt(s, f),
1241			},
1242
1243			(Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Chainable(_, _)) => quote! {
1244				#error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args),
1245			},
1246
1247			(Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Chainable(_, _)) if is_closure(custom_display) => quote! {
1248				#error_kind_name::#variant_ident(ref kind) => {
1249					#[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1250					{ (#custom_display)(kind) }
1251				},
1252			},
1253
1254			(Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Chainable(_, _)) => quote! {
1255				#error_kind_name::#variant_ident(ref kind) => #custom_display(f, kind),
1256			},
1257
1258			(Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Foreign(_)) => quote! {
1259				#error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args),
1260			},
1261
1262			(Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Foreign(_)) if is_closure(custom_display) => quote! {
1263				#error_kind_name::#variant_ident(ref err) => {
1264					#[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1265					{ (#custom_display)(err) }
1266				},
1267			},
1268
1269			(Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Foreign(_)) => quote! {
1270				#error_kind_name::#variant_ident(ref err) => #custom_display(f, err),
1271			},
1272
1273			(Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Custom) => quote! {
1274				#error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args),
1275			},
1276
1277			(Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Custom) => {
1278				let pattern = fields_pattern(&self.variant_fields);
1279				let args = args(&self.variant_fields);
1280
1281				if is_closure(custom_display) {
1282					quote! {
1283						#error_kind_name::#variant_ident #pattern => {
1284							#[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1285							{ (#custom_display)(#args) }
1286						},
1287					}
1288				}
1289				else {
1290					quote! {
1291						#error_kind_name::#variant_ident #pattern => #custom_display(f, #args),
1292					}
1293				}
1294			},
1295
1296			(None, &LinkType::Chainable(_, _)) => quote! {
1297				#error_kind_name::#variant_ident(ref kind) => ::std::fmt::Display::fmt(kind, f),
1298			},
1299
1300			(None, &LinkType::Foreign(_)) => quote! {
1301				#error_kind_name::#variant_ident(ref err) => ::std::fmt::Display::fmt(err, f),
1302			},
1303
1304			(None, &LinkType::Custom) => {
1305				let pattern = fields_pattern_ignore(&self.variant_fields);
1306
1307				quote! {
1308					#error_kind_name::#variant_ident #pattern => ::std::fmt::Display::fmt(self.description(), f),
1309				}
1310			},
1311		}
1312	}
1313
1314	fn error_kind_from_impl(
1315		&self,
1316		error_kind_name: &proc_macro2::Ident,
1317		impl_generics: &syn::ImplGenerics, impl_generics_lifetime: &syn::ImplGenerics, ty_generics: &syn::TypeGenerics, where_clause: Option<&syn::WhereClause>,
1318	) -> Option<proc_macro2::TokenStream> {
1319		let variant_ident = &self.variant_ident;
1320
1321		match self.link_type {
1322			LinkType::Msg => Some(quote! {
1323				impl #impl_generics_lifetime From<&'__a str> for #error_kind_name #ty_generics #where_clause {
1324					fn from(s: &'__a str) -> Self { #error_kind_name::#variant_ident(s.to_string()) }
1325				}
1326
1327				impl #impl_generics From<String> for #error_kind_name #ty_generics #where_clause {
1328					fn from(s: String) -> Self { #error_kind_name::#variant_ident(s) }
1329				}
1330			}),
1331
1332			LinkType::Chainable(_, ref error_kind_ty) => Some(quote! {
1333				impl #impl_generics From<#error_kind_ty> for #error_kind_name #ty_generics #where_clause {
1334					fn from(kind: #error_kind_ty) -> Self {
1335						#error_kind_name::#variant_ident(kind)
1336					}
1337				}
1338			}),
1339
1340			LinkType::Foreign(_) |
1341			LinkType::Custom => None,
1342		}
1343	}
1344
1345	fn error_cause_case(
1346		&self,
1347		error_kind_name: &proc_macro2::Ident,
1348	) -> Option<proc_macro2::TokenStream> {
1349		let variant_ident = &self.variant_ident;
1350
1351		#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
1352		match (self.custom_cause.as_ref(), &self.link_type) {
1353			(_, &LinkType::Msg) => None,
1354
1355			(Some(custom_cause), _) => Some({
1356				let pattern = fields_pattern(&self.variant_fields);
1357				let args = args(&self.variant_fields);
1358
1359				if is_closure(custom_cause) {
1360					quote! {
1361						#error_kind_name::#variant_ident #pattern => {
1362							#[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
1363							let result = (#custom_cause)(#args);
1364							Some(result)
1365						},
1366					}
1367				}
1368				else {
1369					quote! {
1370						#error_kind_name::#variant_ident #pattern => Some(#custom_cause(#args)),
1371					}
1372				}
1373			}),
1374
1375			(None, &LinkType::Foreign(_)) => Some(quote! {
1376				#error_kind_name::#variant_ident(ref err) => ::std::error::Error::cause(err),
1377			}),
1378
1379			(None, &LinkType::Chainable(_, _)) |
1380			(None, &LinkType::Custom) => None,
1381		}
1382	}
1383
1384	fn error_from_impl(
1385		&self,
1386		error_kind_name: &proc_macro2::Ident, error_name: &proc_macro2::Ident,
1387		generics: &std::collections::HashSet<&proc_macro2::Ident>,
1388		impl_generics: &syn::ImplGenerics, impl_generics_lifetime: &syn::ImplGenerics, ty_generics: &syn::TypeGenerics, where_clause: Option<&syn::WhereClause>,
1389	) -> Option<proc_macro2::TokenStream> {
1390		let variant_ident = &self.variant_ident;
1391
1392		match self.link_type {
1393			LinkType::Msg => Some(quote! {
1394				impl #impl_generics_lifetime From<&'__a str> for #error_name #ty_generics #where_clause {
1395					fn from(s: &'__a str) -> Self { Self::from_kind(s.into()) }
1396				}
1397
1398				impl #impl_generics From<String> for #error_name #ty_generics #where_clause {
1399					fn from(s: String) -> Self { Self::from_kind(s.into()) }
1400				}
1401			}),
1402
1403			LinkType::Chainable(ref error_ty, _) => Some(quote! {
1404				impl #impl_generics From<#error_ty> for #error_name #ty_generics #where_clause {
1405					fn from(err: #error_ty) -> Self {
1406						#error_name(#error_kind_name::#variant_ident(err.0), err.1)
1407					}
1408				}
1409			}),
1410
1411			// Don't emit From impl for any generics of the errorkind because they cause conflicting trait impl errors.
1412			// ie don't emit `impl From<T> for Error<T>` even if there's a variant `SomeError(T)`
1413			LinkType::Foreign(syn::Type::Path(syn::TypePath { ref path, .. }))
1414				if !path.global() && path.segments.len() == 1 && generics.contains(&path.segments[0].ident) => None,
1415
1416			LinkType::Foreign(ref ty) => Some(quote! {
1417				impl #impl_generics From<#ty> for #error_name #ty_generics #where_clause {
1418					fn from(err: #ty) -> Self {
1419						Self::from_kind(#error_kind_name::#variant_ident(err))
1420					}
1421				}
1422			}),
1423
1424			LinkType::Custom => None,
1425		}
1426	}
1427
1428	fn chained_error_extract_backtrace_case(&self) -> Option<proc_macro2::TokenStream> {
1429		match self.link_type {
1430			LinkType::Chainable(ref error_ty, _) => Some(quote! {
1431				if let Some(err) = err.downcast_ref::<#error_ty>() {
1432					return err.1.backtrace.clone();
1433				}
1434			}),
1435
1436			LinkType::Msg |
1437			LinkType::Foreign(_) |
1438			LinkType::Custom => None,
1439		}
1440	}
1441}
1442
1443enum CustomFormatter {
1444	FormatString { format_string: String, pattern: proc_macro2::TokenStream, args: proc_macro2::TokenStream },
1445	Expr(syn::Expr),
1446}
1447
1448impl CustomFormatter {
1449	fn parse(tokens: proc_macro2::TokenStream, attr_name: &str, variant_ident: &proc_macro2::Ident, variant_fields: &syn::Fields) -> Self {
1450		let err = match syn::parse(tokens.clone().into()) {
1451			Ok(expr) => return CustomFormatter::Expr(expr),
1452			Err(err) => err,
1453		};
1454
1455		let mut tts = tokens.into_iter();
1456
1457		match tts.next() {
1458			Some(proc_macro2::TokenTree::Ident(ref ident)) if ident == "const" => (),
1459
1460			Some(tt) => panic!(
1461				"Could not parse `{}` attribute of member {}. Expression - {}. Format string - expected `const` but got {}",
1462				attr_name, variant_ident, err, tt),
1463
1464			_ => panic!(
1465				"Could not parse `{}` attribute of member {}. Expression - {}. Format string - expected `const`",
1466				attr_name, variant_ident, err),
1467		}
1468
1469		let value = match tts.next() {
1470			Some(proc_macro2::TokenTree::Group(ref group)) if group.delimiter() == proc_macro2::Delimiter::Parenthesis => group.stream(),
1471
1472			Some(tt) => panic!(
1473				"Could not parse `{}` attribute of member {} - expected `(string literal)` but got {}",
1474				attr_name, variant_ident, tt),
1475
1476			_ => panic!(
1477				"Could not parse `{}` attribute of member {} - expected `(string literal)`",
1478				attr_name, variant_ident),
1479		};
1480
1481		let format_string = match syn::parse2(value) {
1482			Ok(syn::Lit::Str(value)) => value.value(),
1483
1484			Ok(lit) => panic!(
1485				"Could not parse `{}` attribute of member {} - expected string literal but got {}",
1486				attr_name, variant_ident, quote!(#lit).to_string()),
1487
1488			Err(err) => panic!(
1489				"Could not parse `{}` attribute of member {} - {}",
1490				attr_name, variant_ident, err),
1491		};
1492
1493		if let Some(tt) = tts.next() {
1494			panic!(
1495				"Could not parse `{}` attribute of member {} - unexpected token {} after string literal",
1496				attr_name, variant_ident, tt);
1497		}
1498
1499		match *variant_fields {
1500			syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => {
1501				let referenced_names = get_parameter_names(&format_string).unwrap_or_else(|err| panic!(
1502					"Could not parse `{}` attribute of member {} - {}",
1503					attr_name, variant_ident, err));
1504
1505				let (patterns, args): (Vec<_>, Vec<_>) = named.into_iter().map(|f| {
1506					let field_name = f.ident.as_ref().unwrap();
1507					if referenced_names.contains(field_name) {
1508						(quote!(ref #field_name), quote!(#field_name = #field_name,))
1509					}
1510					else {
1511						let ignored_field_name = proc_macro2::Ident::new(&format!("_{}", field_name), proc_macro2::Span::call_site());
1512						(quote!(#field_name: ref #ignored_field_name), quote!())
1513					}
1514				}).unzip();
1515
1516				CustomFormatter::FormatString {
1517					format_string,
1518					pattern: quote!({ #(#patterns,)* }),
1519					args: quote!(#(#args)*),
1520				}
1521			},
1522
1523			syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => {
1524				let referenced_positions = get_parameter_positions(&format_string).unwrap_or_else(|err| panic!(
1525					"Could not parse `{}` attribute of member {} - {}",
1526					attr_name, variant_ident, err));
1527
1528				let (patterns, args): (Vec<_>, Vec<_>) = unnamed.into_iter().enumerate().map(|(i, _)| {
1529					if referenced_positions.contains(&i) {
1530						let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site());
1531						(quote!(ref #field_name), quote!(#field_name,))
1532					}
1533					else {
1534						(quote!(_), quote!())
1535					}
1536				}).unzip();
1537
1538				CustomFormatter::FormatString {
1539					format_string,
1540					pattern: quote!((#(#patterns,)*)),
1541					args: quote!(#(#args)*),
1542				}
1543			},
1544
1545			syn::Fields::Unit => {
1546				ensure_no_parameters(&format_string).unwrap_or_else(|err| panic!(
1547					"Could not parse `{}` attribute of member {} - {}",
1548					attr_name, variant_ident, err));
1549
1550				CustomFormatter::FormatString {
1551					format_string,
1552					pattern: quote!(),
1553					args: quote!(),
1554				}
1555			},
1556		}
1557	}
1558}
1559
1560fn is_error_chain_attribute(attr: &syn::Attribute) -> bool {
1561	if !attr.path.global() && attr.path.segments.len() == 1 {
1562		let segment = &attr.path.segments[0];
1563		return segment.ident == "error_chain" && segment.arguments.is_empty();
1564	}
1565
1566	false
1567}
1568
1569fn is_closure(expr: &syn::Expr) -> bool {
1570	if let syn::Expr::Closure(..) = *expr {
1571		true
1572	}
1573	else {
1574		false
1575	}
1576}
1577
1578fn fields_pattern(variant_fields: &syn::Fields) -> proc_macro2::TokenStream {
1579	match *variant_fields {
1580		syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => {
1581			let fields = named.into_iter().map(|f| {
1582				let field_name = f.ident.as_ref().unwrap();
1583				quote!(ref #field_name)
1584			});
1585			quote!({ #(#fields,)* })
1586		},
1587
1588		syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => {
1589			let fields = unnamed.into_iter().enumerate().map(|(i, _)| {
1590				let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site());
1591				quote!(ref #field_name)
1592			});
1593			quote!((#(#fields,)*))
1594		},
1595
1596		syn::Fields::Unit => quote!(),
1597	}
1598}
1599
1600fn fields_pattern_ignore(variant_fields: &syn::Fields) -> proc_macro2::TokenStream {
1601	match *variant_fields {
1602		syn::Fields::Named(syn::FieldsNamed { .. }) => quote!({ .. }),
1603		syn::Fields::Unnamed(_) => quote!((..)),
1604		syn::Fields::Unit => quote!(),
1605	}
1606}
1607
1608fn args(variant_fields: &syn::Fields) -> proc_macro2::TokenStream {
1609	match *variant_fields {
1610		syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => {
1611			let fields = named.into_iter().map(|f| {
1612				let field_name = f.ident.as_ref().unwrap();
1613				quote!(#field_name)
1614			});
1615			quote!(#(#fields,)*)
1616		},
1617
1618		syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => {
1619			let fields = unnamed.into_iter().enumerate().map(|(i, _)| {
1620				let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site());
1621				quote!(#field_name)
1622			});
1623			quote!(#(#fields,)*)
1624		},
1625
1626		syn::Fields::Unit => quote!(),
1627	}
1628}
1629
1630fn get_parameter_names(format_string: &str) -> Result<std::collections::HashSet<proc_macro2::Ident>, String> {
1631	let parser = syntex_fmt_macros::Parser::new(format_string);
1632
1633	parser
1634	.filter_map(|piece| match piece {
1635		syntex_fmt_macros::Piece::String(_) => None,
1636
1637		syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position {
1638			syntex_fmt_macros::Position::ArgumentNext => Some(Err("expected named parameter but found `{}`".to_string())),
1639			syntex_fmt_macros::Position::ArgumentIs(index) => Some(Err(format!("expected named parameter but found `{{{}}}`", index))),
1640			syntex_fmt_macros::Position::ArgumentNamed(name) => Some(syn::parse_str(name).map_err(|err| format!("could not parse named parameter `{{{}}}` - {}", name, err))),
1641		},
1642	})
1643	.collect()
1644}
1645
1646fn get_parameter_positions(format_string: &str) -> Result<std::collections::HashSet<usize>, String> {
1647	let parser = syntex_fmt_macros::Parser::new(format_string);
1648
1649	parser
1650	.filter_map(|piece| match piece {
1651		syntex_fmt_macros::Piece::String(_) => None,
1652
1653		syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position {
1654			syntex_fmt_macros::Position::ArgumentNext => Some(Err("expected positional parameter but found `{}`".to_string())),
1655			syntex_fmt_macros::Position::ArgumentIs(index) => Some(Ok(index)),
1656			syntex_fmt_macros::Position::ArgumentNamed(name) => Some(Err(format!("expected positional parameter but found `{{{}}}`", name))),
1657		},
1658	})
1659	.collect()
1660}
1661
1662fn ensure_no_parameters(format_string: &str) -> Result<(), String> {
1663	let parser = syntex_fmt_macros::Parser::new(format_string);
1664
1665	for piece in parser {
1666		match piece {
1667			syntex_fmt_macros::Piece::String(_) => (),
1668
1669			syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position {
1670				syntex_fmt_macros::Position::ArgumentNext => return Err("expected no parameters but found `{}`".to_string()),
1671				syntex_fmt_macros::Position::ArgumentIs(index) => return Err(format!("expected no parameters but found `{{{}}}`", index)),
1672				syntex_fmt_macros::Position::ArgumentNamed(name) => return Err(format!("expected no parameters but found `{{{}}}`", name)),
1673			},
1674		}
1675	}
1676
1677	Ok(())
1678}