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