error_enum_macros/
lib.rs

1//! # `error-enum-macros`
2//!
3//! A procedural macro crate for [`error-enum`](https://crates.io/crates/error-enum) to define error enums
4//! with rich diagnostics support.
5//!
6//! Please refer to [`error-enum`](https://crates.io/crates/error-enum) and
7//! [`its documentation`](https://docs.rs/error-enum/) for more details.
8#![warn(unused_crate_dependencies)]
9
10use either::Either;
11use lazy_regex::{lazy_regex, Lazy, Regex};
12use proc_macro::TokenStream;
13use proc_macro2::{Span, TokenStream as TokenStream2};
14use quote::{format_ident, quote, ToTokens};
15use std::borrow::Cow;
16use syn::{
17    braced,
18    parse::{self, Parse},
19    parse_macro_input, parse_quote,
20    punctuated::{self, Punctuated},
21    token::{self, Brace},
22    Attribute, DeriveInput, Error, Fields, Generics, Ident, LitStr, Result, Token, Type, Variant,
23    Visibility,
24};
25
26#[cfg(test)]
27mod tests;
28
29/// A tuple type with 4 identical types.
30///
31/// For `impl_error_enum_branch` and `impl_error_enum`,
32/// it means `(kind, number, code, primary_span)`.
33type Tuple4<T> = (T, T, T, T);
34
35fn split_fields_attrs(fields: &mut Fields) -> Result<Option<Ident>> {
36    let mut span_ident = None;
37    for (idx, field) in fields.iter_mut().enumerate() {
38        for attr in &field.attrs {
39            if attr.meta.path().is_ident("diag") {
40                attr.parse_nested_meta(|meta| {
41                    if meta.path.is_ident("span") {
42                        span_ident = Some(field.ident.clone().unwrap_or(format_ident!("_{idx}")))
43                    }
44                    Ok(())
45                })?
46            }
47        }
48        field.attrs.retain(|attr| !attr.path().is_ident("diag"));
49    }
50    Ok(span_ident)
51}
52
53/// Tree node of error definitions.
54enum ErrorTree {
55    /// Prefix node.
56    Prefix {
57        span: Span,
58        attrs: Vec<Attribute>,
59        nodes: Punctuated<ErrorTree, Token![,]>,
60    },
61    /// Leaf node.
62    ///
63    /// See [`syn::Variant`] and [`syn::DataStruct`].
64    Variant {
65        span: Span,
66        attrs: Vec<Attribute>,
67        span_ident: Option<Ident>,
68        ident: Ident,
69        fields: Fields,
70    },
71}
72
73impl ErrorTree {
74    fn attrs(&self) -> &[Attribute] {
75        match self {
76            ErrorTree::Prefix { attrs, .. } => attrs,
77            ErrorTree::Variant { attrs, .. } => attrs,
78        }
79    }
80    fn ident(&self) -> Option<&Ident> {
81        match self {
82            ErrorTree::Prefix { .. } => None,
83            ErrorTree::Variant { ident, .. } => Some(ident),
84        }
85    }
86    fn fields(&self) -> Option<&Fields> {
87        match self {
88            ErrorTree::Prefix { .. } => None,
89            ErrorTree::Variant { fields, .. } => Some(fields),
90        }
91    }
92    fn span_ident(&self) -> Option<Ident> {
93        match self {
94            ErrorTree::Prefix { .. } => None,
95            ErrorTree::Variant { span_ident, .. } => span_ident.clone(),
96        }
97    }
98    fn span(&self) -> Span {
99        match self {
100            ErrorTree::Prefix { span, .. } => *span,
101            ErrorTree::Variant { span, .. } => *span,
102        }
103    }
104}
105
106impl Parse for ErrorTree {
107    /// See [`Variant::parse`].
108    fn parse(input: parse::ParseStream) -> syn::Result<Self> {
109        let attrs = input.call(Attribute::parse_outer)?;
110
111        if input.peek(Ident) {
112            let ident: Ident = input.parse()?;
113            let mut fields = if input.peek(token::Brace) {
114                Fields::Named(input.parse()?)
115            } else if input.peek(token::Paren) {
116                Fields::Unnamed(input.parse()?)
117            } else {
118                Fields::Unit
119            };
120            let span_ident = split_fields_attrs(&mut fields)?;
121            Ok(ErrorTree::Variant {
122                span: ident.span(),
123                attrs,
124                span_ident,
125                ident,
126                fields,
127            })
128        } else {
129            let span = input.span();
130            let children;
131            braced!(children in input);
132            let nodes = Punctuated::parse_terminated(&children)?;
133            Ok(ErrorTree::Prefix { span, attrs, nodes })
134        }
135    }
136}
137
138#[derive(Clone, Copy, Default)]
139enum Kind {
140    #[default]
141    Error,
142    Warn,
143}
144
145impl Kind {
146    fn short_str(&self) -> &'static str {
147        match self {
148            Kind::Error => "E",
149            Kind::Warn => "W",
150        }
151    }
152}
153
154impl TryFrom<LitStr> for Kind {
155    type Error = Error;
156
157    fn try_from(value: LitStr) -> Result<Self> {
158        match value.value().as_str() {
159            "error" | "Error" => Ok(Kind::Error),
160            "warn" | "Warn" => Ok(Kind::Warn),
161            _ => Err(Error::new_spanned(
162                value,
163                "Kind must be either `Error` or `Warn`.",
164            )),
165        }
166    }
167}
168
169impl ToTokens for Kind {
170    fn to_tokens(&self, tokens: &mut TokenStream2) {
171        let kind = match self {
172            Kind::Error => quote! { ::error_enum::Kind::Error },
173            Kind::Warn => quote! { ::error_enum::Kind::Warn },
174        };
175        tokens.extend(kind);
176    }
177}
178
179/// Configuration for each variant.
180#[derive(Clone)]
181struct Config {
182    kind: Option<Kind>,
183    number: String,
184    msg: Option<LitStr>,
185    attrs: Vec<Attribute>,
186    ident: Option<Ident>,
187    fields: Option<Fields>,
188    span_field: Option<Ident>,
189    // FIXME: move to `ErrorEnum` for better performance?
190    span_type: Option<Type>,
191    label: Option<LitStr>,
192    depth: usize,
193    nested: bool,
194    #[expect(unused)]
195    span: Span,
196}
197
198impl Config {
199    const fn new(span: Span) -> Self {
200        Self {
201            kind: None,
202            number: String::new(),
203            msg: None,
204            attrs: Vec::new(),
205            ident: None,
206            fields: None,
207            span_field: None,
208            span_type: None,
209            label: None,
210            depth: 0,
211            nested: false,
212            span,
213        }
214    }
215    fn process(
216        &self,
217        attrs: &[Attribute],
218        ident: Option<&Ident>,
219        fields: Option<&Fields>,
220        span_field: Option<Ident>,
221        span: Span,
222    ) -> Result<Self> {
223        let mut kind = self.kind;
224        let mut number = self.number.clone();
225        let mut msg = self.msg.clone();
226        let mut label = self.label.clone();
227        let mut span_type = self.span_type.clone();
228        let depth = self.depth + 1;
229        let mut nested = self.nested;
230        let mut unused_attrs = Vec::new();
231
232        for attr in attrs {
233            if attr.path().is_ident("diag") {
234                attr.parse_nested_meta(|meta| {
235                    if meta.path.is_ident("kind") {
236                        let value: LitStr = meta.value()?.parse()?;
237                        kind = Some(value.try_into()?);
238                    } else if meta.path.is_ident("label") {
239                        let value: LitStr = meta.value()?.parse()?;
240                        label = Some(value);
241                    } else if meta.path.is_ident("msg") {
242                        let value: LitStr = meta.value()?.parse()?;
243                        msg = Some(value);
244                    } else if meta.path.is_ident("nested") {
245                        nested = true;
246                    } else if meta.path.is_ident("number") {
247                        let value: LitStr = meta.value()?.parse()?;
248                        number.push_str(value.value().as_str());
249                    } else if meta.path.is_ident("span_type") {
250                        let value: LitStr = meta.value()?.parse()?;
251                        span_type = Some(value.parse()?);
252                    } else {
253                        return Err(Error::new_spanned(meta.path, "Unknown attribute key."));
254                    }
255                    Ok(())
256                })?
257            } else {
258                unused_attrs.push(attr.clone());
259            }
260        }
261
262        let ident = ident.cloned();
263        let fields = fields.cloned();
264        Ok(Self {
265            kind,
266            number,
267            msg,
268            attrs: unused_attrs,
269            ident,
270            fields,
271            span_field,
272            span_type,
273            label,
274            depth,
275            nested,
276            span,
277        })
278    }
279}
280
281struct ErrorTreeIter<'i> {
282    stack: Vec<(punctuated::Iter<'i, ErrorTree>, Config)>,
283}
284
285impl<'i> ErrorTreeIter<'i> {
286    fn new(tree: punctuated::Iter<'i, ErrorTree>, config: Config) -> Result<Self> {
287        Ok(Self {
288            stack: vec![(tree, config)],
289        })
290    }
291    fn process_next(node: &'i ErrorTree, config: &Config, span: Span) -> Result<Config> {
292        let new_config = config.process(
293            node.attrs(),
294            node.ident(),
295            node.fields(),
296            node.span_ident(),
297            span,
298        )?;
299        Ok(new_config)
300    }
301}
302
303impl<'i> Iterator for ErrorTreeIter<'i> {
304    type Item = Result<Config>;
305
306    fn next(&mut self) -> Option<Self::Item> {
307        while let Some((slice, config)) = self.stack.last_mut() {
308            if let Some(node) = slice.next() {
309                let config = Self::process_next(node, config, node.span())
310                    .map(Some)
311                    .transpose()?;
312                if let Ok(config) = &config {
313                    if let ErrorTree::Prefix { nodes, .. } = node {
314                        self.stack.push((nodes.iter(), config.clone()));
315                    }
316                }
317                return Some(config);
318            } else {
319                self.stack.pop();
320            }
321        }
322        None
323    }
324}
325
326enum ErrorEnumInner {
327    Multiple {
328        brace: Brace,
329        roots: Punctuated<ErrorTree, Token![,]>,
330        body: bool,
331    },
332    Single {
333        node: ErrorTree,
334    },
335}
336
337impl ErrorEnumInner {
338    fn iter(&self, config: Config) -> Result<impl Iterator<Item = Result<Config>> + '_> {
339        match self {
340            ErrorEnumInner::Multiple { roots, .. } => {
341                Ok(Either::Left(ErrorTreeIter::new(roots.iter(), config)?))
342            }
343            ErrorEnumInner::Single { node } => {
344                let iter = Either::Right(std::iter::once(ErrorTreeIter::process_next(
345                    node,
346                    &config,
347                    node.span(),
348                )));
349                Ok(iter)
350            }
351        }
352    }
353}
354
355/// The entire error enum.
356///
357/// ```ignore
358/// pub ErrorName {
359///     // Variants...
360/// }
361/// ```
362struct ErrorEnum {
363    attrs: Vec<Attribute>,
364    vis: Visibility,
365    name: Ident,
366    generics: Generics,
367    inner: ErrorEnumInner,
368    config: Config,
369}
370
371impl ErrorEnum {
372    fn iter(&self) -> Result<impl Iterator<Item = Result<Config>> + '_> {
373        self.inner.iter(self.config.clone())
374    }
375    fn is_enum(&self) -> bool {
376        matches!(self.inner, ErrorEnumInner::Multiple { .. })
377    }
378}
379
380impl Parse for ErrorEnum {
381    fn parse(input: parse::ParseStream) -> syn::Result<Self> {
382        let mut attrs = input.call(Attribute::parse_outer)?;
383        let vis = input.parse()?;
384        let name: Ident = input.parse()?;
385        let generics = input.parse()?;
386        let children;
387        let brace = braced!(children in input);
388        let config = Config::new(name.span()).process(&attrs, None, None, None, name.span())?;
389        attrs.retain(|attr| !attr.path().is_ident("diag"));
390
391        let roots = Punctuated::parse_terminated(&children)?;
392        let inner = ErrorEnumInner::Multiple {
393            body: true,
394            brace,
395            roots,
396        };
397        Ok(Self {
398            attrs,
399            vis,
400            generics,
401            name,
402            inner,
403            config,
404        })
405    }
406}
407
408impl TryFrom<DeriveInput> for ErrorEnum {
409    type Error = Error;
410
411    fn try_from(value: DeriveInput) -> Result<Self> {
412        let DeriveInput {
413            mut attrs,
414            vis,
415            ident,
416            generics,
417            data,
418        } = value;
419        match data {
420            syn::Data::Enum(data_enum) => {
421                let mut roots = Punctuated::new();
422                for pair in data_enum.variants.into_pairs() {
423                    let (mut variant, comma) = pair.into_tuple();
424                    let span = variant.ident.span();
425                    let span_ident = split_fields_attrs(&mut variant.fields)?;
426                    let node = ErrorTree::Variant {
427                        span,
428                        attrs: variant.attrs,
429                        ident: variant.ident,
430                        fields: variant.fields,
431                        span_ident,
432                    };
433                    roots.push_value(node);
434                    if let Some(comma) = comma {
435                        roots.push_punct(comma);
436                    }
437                }
438                let config =
439                    Config::new(ident.span()).process(&attrs, None, None, None, ident.span())?;
440                attrs.retain(|attr| !attr.path().is_ident("diag"));
441
442                let inner = ErrorEnumInner::Multiple {
443                    body: false,
444                    brace: data_enum.brace_token,
445                    roots,
446                };
447                Ok(Self {
448                    attrs,
449                    vis,
450                    name: ident,
451                    generics,
452                    inner,
453                    config,
454                })
455            }
456            syn::Data::Struct(mut data_struct) => {
457                let span = ident.span();
458                let span_ident = split_fields_attrs(&mut data_struct.fields)?;
459                let config =
460                    Config::new(ident.span()).process(&attrs, None, None, None, ident.span())?;
461                attrs.retain(|attr| !attr.path().is_ident("diag"));
462
463                let node = ErrorTree::Variant {
464                    span,
465                    attrs,
466                    ident: ident.clone(),
467                    fields: data_struct.fields,
468                    span_ident,
469                };
470
471                let inner = ErrorEnumInner::Single { node };
472
473                Ok(Self {
474                    attrs: Vec::new(),
475                    vis,
476                    name: ident,
477                    generics,
478                    inner,
479                    config,
480                })
481            }
482            _ => Err(Error::new_spanned(
483                ident,
484                "ErrorEnum can only be derived for enums or structs.",
485            )),
486        }
487    }
488}
489
490impl ErrorEnum {
491    fn variant<'a>(&'a self, ident: &'a Ident) -> impl ToTokens + 'a {
492        struct VariantPrefix<'a> {
493            ident: &'a Ident,
494            is_enum: bool,
495        }
496        impl<'a> ToTokens for VariantPrefix<'a> {
497            fn to_tokens(&self, tokens: &mut TokenStream2) {
498                let ident = self.ident;
499                if self.is_enum {
500                    tokens.extend(quote! { Self::#ident });
501                } else {
502                    tokens.extend(quote! { Self });
503                }
504            }
505        }
506
507        VariantPrefix {
508            ident,
509            is_enum: self.is_enum(),
510        }
511    }
512    fn doc(&self) -> Result<Vec<String>> {
513        self.iter()?
514            .map(|config| {
515                let Config {
516                    number,
517                    depth,
518                    ident,
519                    msg,
520                    kind,
521                    ..
522                } = config?;
523                let indent = "  ".repeat(depth - 2);
524                let msg = msg.as_ref().map(|s| s.value());
525                let kind = kind.unwrap_or_default().short_str();
526                Ok(match (ident, msg) {
527                    (Some(ident), Some(msg)) => {
528                        format!("{indent}- `{kind}{number}`(**{ident}**): {msg}")
529                    }
530                    (None, Some(msg)) => format!("{indent}- `{kind}{number}`: {msg}"),
531                    (Some(ident), None) => format!("{indent}- `{kind}{number}`(**{ident}**)"),
532                    (None, None) => format!("{indent}- `{kind}{number}`"),
533                })
534            })
535            .collect()
536    }
537    fn variants(&self) -> Result<Vec<Variant>> {
538        self.iter()?
539            .filter_map(|config| {
540                config
541                    .map(
542                        |Config {
543                             kind,
544                             msg,
545                             number,
546                             attrs,
547                             ident,
548                             fields,
549                             ..
550                         }| {
551                            Some((kind, msg, number, attrs, ident?, fields?))
552                        },
553                    )
554                    .transpose()
555            })
556            .map(|config| {
557                let (kind, msg, number, mut attrs, ident, fields) = config?;
558
559                let kind = kind.unwrap_or_default();
560                let code = format!("{}{}", kind.short_str(), number);
561
562                let doc = match msg {
563                    Some(msg) => {
564                        format!("`{code}`: {msg}", msg = msg.value())
565                    }
566                    None => format!("`{code}`"),
567                };
568
569                attrs.push(syn::parse_quote! {
570                    #[doc = #doc]
571                });
572                attrs.push(syn::parse_quote! {
573                    #[doc(alias = #code)]
574                });
575
576                Ok(Variant {
577                    attrs,
578                    ident,
579                    fields,
580                    discriminant: None,
581                })
582            })
583            .collect()
584    }
585    fn used_unnamed_fields(msg: &LitStr) -> Result<Vec<Ident>> {
586        static ARG: Lazy<Regex> = lazy_regex!(r#"(^|[^\{])(\{\{)*\{(?<index>\d+)(:[^\{\}]*)?\}"#);
587        ARG.captures_iter(msg.value().as_str())
588            .map(|cap| {
589                let index = cap
590                    .name("index")
591                    .ok_or_else(|| Error::new_spanned(msg, "Invalid argument index."))?
592                    .as_str()
593                    .parse::<usize>()
594                    .map_err(|err| {
595                        Error::new_spanned(msg, format!("Invalid argument index: {err}"))
596                    })?;
597                Ok(format_ident!("_{}", index))
598            })
599            .collect()
600    }
601    fn display_branch(&self, ident: &Ident, fields: &Fields, msg: &LitStr) -> Result<TokenStream2> {
602        let prefix = self.variant(ident);
603        match fields {
604            Fields::Named(named) => {
605                let members = named.named.iter().map(|f| f.ident.as_ref());
606                Ok(quote! {
607                    #[allow(unused_variables)]
608                    #prefix { #(#members),* } => ::core::write!(f, #msg),
609                })
610            }
611            Fields::Unnamed(unnamed) => {
612                let params = (0..unnamed.unnamed.len()).map(|i| format_ident!("_{}", i));
613                let args = Self::used_unnamed_fields(msg)?;
614                Ok(quote! {
615                    #prefix ( #(#params),* ) => ::core::write!(f, #msg #(, #args)* ),
616                })
617            }
618            Fields::Unit => Ok(quote! {
619                #prefix => ::core::write!(f, #msg),
620            }),
621        }
622    }
623    fn display(&self) -> Result<Vec<TokenStream2>> {
624        self.iter()?
625            .filter_map(|config| {
626                config
627                    .map(
628                        |Config {
629                             msg, ident, fields, ..
630                         }| { Some((msg, ident?, fields?)) },
631                    )
632                    .transpose()
633            })
634            .map(|config| {
635                let (msg, ident, fields) = config?;
636                let msg = msg.ok_or_else(|| {
637                    Error::new_spanned(
638                        &ident,
639                        "Missing message. Consider using `#[diag(msg = \"...\")]`",
640                    )
641                })?;
642                self.display_branch(&ident, &fields, &msg)
643            })
644            .collect()
645    }
646    fn primary_label_branch(
647        &self,
648        ident: &Ident,
649        fields: &Fields,
650        label: &LitStr,
651    ) -> Result<TokenStream2> {
652        let prefix = self.variant(ident);
653        match fields {
654            Fields::Named(named) => {
655                let members = named.named.iter().map(|f| f.ident.as_ref());
656                Ok(quote! {
657                    #[allow(unused_variables)]
658                    #prefix { #(#members),* } => ::std::format!(#label),
659                })
660            }
661            Fields::Unnamed(unnamed) => {
662                let params = (0..unnamed.unnamed.len()).map(|i| format_ident!("_{}", i));
663                let args = Self::used_unnamed_fields(label)?;
664                Ok(quote! {
665                    #prefix ( #(#params),* ) => ::std::format!(#label #(, #args)* ),
666                })
667            }
668            Fields::Unit => Ok(quote! {
669                #prefix => ::std::format!(#label),
670            }),
671        }
672    }
673    fn primary_label(&self) -> Result<Vec<TokenStream2>> {
674        self.iter()?
675            .filter_map(|config| {
676                config
677                    .map(
678                        |Config {
679                             msg,
680                             ident,
681                             fields,
682                             label,
683                             ..
684                         }| { Some((msg, ident?, fields?, label)) },
685                    )
686                    .transpose()
687            })
688            .map(|config| {
689                let (msg, ident, fields, label) = config?;
690                let label = label.or(msg).ok_or_else(|| {
691                    Error::new_spanned(
692                        &ident,
693                        "Missing label or message. Consider using `#[diag(label = \"...\")]`",
694                    )
695                })?;
696                self.primary_label_branch(&ident, &fields, &label)
697            })
698            .collect()
699    }
700    fn impl_error_enum_branch(
701        &self,
702        ident: &Ident,
703        fields: &Fields,
704        span_field: Option<Ident>,
705        kind: &Kind,
706        number: &str,
707    ) -> Result<Tuple4<TokenStream2>> {
708        let branch_ignored = match fields {
709            Fields::Named(_) => quote! { { .. } },
710            Fields::Unnamed(_) => quote! { (..) },
711            Fields::Unit => quote! {},
712        };
713        let code = format!("{}{}", kind.short_str(), number);
714
715        let prefix = self.variant(ident);
716
717        let kind = quote! {
718            #prefix #branch_ignored => #kind,
719        };
720        let number = quote! {
721            #prefix #branch_ignored => #number,
722        };
723        let code = quote! {
724            #prefix #branch_ignored => #code,
725        };
726        let span_type = self.span_type();
727        let span = if let Some(span_field) = span_field {
728            quote! {<#span_type as ::core::convert::From<_>>::from(#span_field)}
729        } else {
730            quote! {<#span_type as ::core::default::Default>::default()}
731        };
732        let primary_span = match fields {
733            Fields::Named(named) => {
734                let members = named.named.iter().map(|f| f.ident.as_ref());
735                quote! {
736                    #[allow(unused_variables)]
737                    #prefix { #(#members),* } => #span,
738                }
739            }
740            Fields::Unnamed(unnamed) => {
741                let params = (0..unnamed.unnamed.len()).map(|i| format_ident!("_{}", i));
742                quote! {
743                    #[allow(unused_variables)]
744                    #prefix ( #(#params),* ) => #span,
745                }
746            }
747            Fields::Unit => quote! {
748                #prefix => #span,
749            },
750        };
751        Ok((kind, number, code, primary_span))
752    }
753    fn impl_error_enum(&self) -> Result<Tuple4<Vec<TokenStream2>>> {
754        self.iter()?
755            .filter_map(|config| {
756                config
757                    .map(
758                        |Config {
759                             ident,
760                             fields,
761                             span_field,
762                             kind,
763                             number,
764                             ..
765                         }| {
766                            Some((ident?, fields?, span_field, kind, number))
767                        },
768                    )
769                    .transpose()
770            })
771            .map(|config| {
772                let (ident, fields, span_field, kind, number) = config?;
773                let kind = kind.unwrap_or_default();
774                self.impl_error_enum_branch(&ident, &fields, span_field, &kind, &number)
775            })
776            .collect()
777    }
778    fn span_type(&self) -> Cow<'_, Type> {
779        self.config.span_type.as_ref().map_or_else(
780            || {
781                Cow::Owned(parse_quote! {
782                    ::error_enum::SimpleSpan
783                })
784            },
785            Cow::Borrowed,
786        )
787    }
788    fn try_to_tokens(&self, tokens: &mut TokenStream2) -> Result<()> {
789        let attrs = &self.attrs;
790        let vis = &self.vis;
791        let name = &self.name;
792        let generics = &self.generics;
793
794        let doc = self.doc()?;
795
796        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
797
798        let variants = self.variants()?;
799
800        if let ErrorEnumInner::Multiple {
801            body: true, brace, ..
802        } = self.inner
803        {
804            tokens.extend(quote! {
805                #(#attrs)*
806                #[doc = "List of error variants:"]
807                #(
808                    #[doc = #doc]
809                )*
810                #vis enum #name #generics
811            });
812            brace.surround(tokens, |tokens| {
813                tokens.extend(quote! { #(#variants, )* });
814            });
815        }
816
817        let display = self.display()?;
818        tokens.extend(quote! {
819            impl #impl_generics ::core::fmt::Display for #name #ty_generics #where_clause {
820                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
821                    match self {
822                        #(#display)*
823                    }
824                }
825            }
826            impl #impl_generics ::core::error::Error for #name #ty_generics #where_clause {}
827        });
828
829        let (kind, number, code, primary_span) = self.impl_error_enum()?;
830        let primary_label = self.primary_label()?;
831        let span_type = self.span_type();
832        tokens.extend(quote! {
833            impl #impl_generics ::error_enum::ErrorType for #name #ty_generics #where_clause {
834                type Span = #span_type;
835                type Message = ::std::string::String;
836
837                fn kind(&self) -> ::error_enum::Kind {
838                    match self {
839                        #(#kind)*
840                    }
841                }
842                fn number(&self) -> &::core::primitive::str {
843                    match self {
844                        #(#number)*
845                    }
846                }
847                fn code(&self) -> &::core::primitive::str {
848                    match self {
849                        #(#code)*
850                    }
851                }
852                fn primary_span(&self) -> #span_type {
853                    match self {
854                        #(#primary_span)*
855                    }
856                }
857                fn primary_message(&self) -> ::std::string::String {
858                    ::std::format!("{self}")
859                }
860                fn primary_label(&self) -> ::std::string::String {
861                    match self {
862                        #(#primary_label)*
863                    }
864                }
865            }
866        });
867
868        Ok(())
869    }
870}
871
872impl ToTokens for ErrorEnum {
873    fn to_tokens(&self, tokens: &mut TokenStream2) {
874        let mut buffer = TokenStream2::new();
875        self.try_to_tokens(&mut buffer)
876            .inspect(|()| tokens.extend(buffer))
877            .unwrap_or_else(|err| {
878                let diag = err.to_compile_error();
879                tokens.extend(diag);
880            });
881    }
882}
883
884/// Define a new layered error type.
885///
886/// Syntax:
887///
888/// ```ignore
889/// $error_type =
890///     $vis:vis $name:ident {
891///         $($variant:variant, )*
892///     }
893///
894/// $variant =
895///   // Prefix node.
896///     #[diag(kind   = $kind:lit_str)]
897///     #[diag(number = $number:lit_int)]
898///     #[diag(msg    = $msg:lit_str)]
899///     {
900///         $($child_variant:variant, )*
901///     }
902///   // Leaf node (three forms).
903///   | #[diag(kind   = $kind:lit_str)]
904///     #[diag(number = $number:lit_int)]
905///     #[diag(msg    = $msg:lit_str)]
906///     $ident:ident (
907///         $(
908///             $(#[diag(span)])?
909///             $field_ty:ty
910///         ),*
911///     )
912///   | #[diag(kind   = $kind:lit_str)]
913///     #[diag(number = $number:lit_int)]
914///     #[diag(msg    = $msg:lit_str)]
915///     $ident:ident {
916///         $(
917///             $(#[diag(span)])?
918///             $field_name:ident: $field_ty:ty
919///         ),*
920///     }
921///   | #[diag(kind   = $kind:lit_str)]
922///     #[diag(number = $number:lit_int)]
923///     #[diag(msg    = $msg:lit_str)]
924///     $ident:ident
925/// ```
926#[proc_macro]
927pub fn error_type(token: TokenStream) -> TokenStream {
928    let error = parse_macro_input!(token as ErrorEnum);
929    error.to_token_stream().into()
930}
931
932/// Implement error capabilities for an existing enum.
933///
934/// See [`error_type!`] for syntax details.
935#[proc_macro_derive(ErrorType, attributes(diag))]
936pub fn error_enum(token: TokenStream) -> TokenStream {
937    let input: DeriveInput = parse_macro_input!(token as DeriveInput);
938    let error = ErrorEnum::try_from(input)
939        .map_or_else(|err| err.to_compile_error(), |e| e.to_token_stream());
940    error.into()
941}