Skip to main content

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