bare_err_tree_proc/
lib.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7//! Derive macros for `bare_err_tree`.
8
9#![cfg_attr(docsrs, feature(doc_auto_cfg))]
10#![cfg_attr(coverage, feature(coverage_attribute))]
11
12extern crate proc_macro;
13use core::panic;
14
15use proc_macro::{Span, TokenStream};
16use quote::{quote, ToTokens};
17use syn::{
18    parse::Parser, parse_macro_input, punctuated::Punctuated, token::Brace, Attribute, Data,
19    DataStruct, DeriveInput, Error, Field, Fields, FieldsNamed, Generics, Ident, Meta, Visibility,
20};
21
22mod errtype;
23use errtype::*;
24mod boiler;
25use boiler::*;
26mod fields;
27use fields::*;
28
29/// Implements a type as an error tree.
30///
31/// The struct must define [`Error`](`core::error::Error`) and be annotated with `#[err_tree]` above
32/// any attributes relying on a full field definition. The type must then be
33/// internally constructed with `Self::_tree` to capture extra error
34/// information in a hidden field.
35///
36/// Any derive such as [`Clone`] that relies on all fields being present must
37/// occur after the `#[err_tree]` macro. The `_err_tree_pkg` field will
38/// otherwise be added late and break the derivation.
39///
40/// # `Self::_tree`
41/// This is an internal-use constructor that takes all struct fields in order.
42/// Use `#[track_caller]` on any functions calling `Self::_tree` to store the
43/// callsite correctly.
44/// [Open an issue or PR](<https://github.com/Bennett-Petzold/bare_err_tree>)
45/// if this hidden field degrades a struct's API (aside from requiring a
46/// constructor method).
47///
48/// #### Example
49/// ```
50/// # #![cfg_attr(coverage, feature(coverage_attribute))]
51/// # use std::{error::Error, fmt::{self, Debug, Display, Formatter}};
52/// use bare_err_tree::{err_tree, tree_unwrap};
53///
54/// #[err_tree]
55/// #[derive(Debug)]
56/// struct Foo {
57///     num: i32,
58/// }
59///
60/// impl Foo {
61/// #   #[cfg_attr(coverage, coverage(off))]
62///     #[track_caller]
63///     pub fn new(num: i32) -> Self {
64///         Foo::_tree(num)
65///     }
66/// }
67///
68/// impl Error for Foo {
69/// #   #[cfg_attr(coverage, coverage(off))]
70///     fn source(&self) -> Option<&(dyn Error + 'static)> {
71///         # /*
72///         ...
73///         # */
74///         # unimplemented!()
75///     }
76/// }
77/// impl Display for Foo {
78/// #   #[cfg_attr(coverage, coverage(off))]
79///     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
80///         # /*
81///         ...
82///         # */
83///         # unimplemented!()
84///     }
85/// }
86/// ```
87///
88/// # Field Annotations
89/// The macro needs annotations for underlying source fields.
90///
91/// #### Single Item
92/// * `tree_err`: Mark a field as a `ErrTree` implementing [`Error`](`core::error::Error`).
93/// * `dyn_err`: Mark a field as a generic [`Error`](`core::error::Error`).
94///
95/// #### Collection
96/// `*_iter_err` works on any type with a `.iter()` method returning its items.
97///
98/// * `tree_iter_err`: Mark a field as a collection of `ErrTree` implementing [`Error`](`core::error::Error`)s.
99/// * `dyn_iter_err`: Mark a field as a collection of generic [`Error`](`core::error::Error`)s.
100///
101/// `*_iter_err` does not allocate for arrays with a known length.
102/// The `derive_alloc` feature enables generation of allocating code to support
103/// dynamically sized collections.
104///
105/// #### Example
106/// ```
107/// # #![cfg_attr(coverage, feature(coverage_attribute))]
108/// # use std::{any::Any, error::Error, fmt::{self, Debug, Display, Formatter}};
109/// use bare_err_tree::{err_tree, tree_unwrap, AsErrTree, ErrTree};
110///
111/// #[err_tree]
112/// #[derive(Debug)]
113/// struct Foo {
114///     #[dyn_err]
115///     io_err: std::io::Error,
116///     #[dyn_iter_err]
117///     extra_io_errs: [std::io::Error; 5],
118/// }
119///
120/// impl Foo {
121/// #   #[cfg_attr(coverage, coverage(off))]
122///     #[track_caller]
123///     pub fn new(io_err: std::io::Error, extra_io_errs: [std::io::Error; 5]) -> Self {
124///         Foo::_tree(io_err, extra_io_errs)
125///     }
126/// }
127///
128/// impl Error for Foo {
129/// #   #[cfg_attr(coverage, coverage(off))]
130///     fn source(&self) -> Option<&(dyn Error + 'static)> {
131///         # /*
132///         ...
133///         # */
134///         # unimplemented!()
135///     }
136/// }
137/// impl Display for Foo {
138/// #   #[cfg_attr(coverage, coverage(off))]
139///     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
140///         # /*
141///         ...
142///         # */
143///         # unimplemented!()
144///     }
145/// }
146///
147/// fn main() {
148///     // Make a Foo of all EOF errors
149///     let eof_gen = || std::io::Error::from(std::io::ErrorKind::UnexpectedEof);
150///     let err = Foo::new(eof_gen(), std::array::from_fn(|_| eof_gen()));
151///
152///     // Confirm exactly six sources from annotation
153///     err.as_err_tree(&mut |tree| {
154///         let sources = tree.sources();
155///         assert_eq!(sources.count(), 6);
156///     });
157/// }
158/// ```
159///
160/// # Generating a Wrapper
161/// `#[err_tree(WRAPPER)]` will generate a wrapper struct for storing metadata.
162/// Enums need this form, as a hidden field cannot be added to the enum.
163/// `WRAPPER` provides [`From`](`core::convert::From`) both ways and
164/// [`Deref`](`core::ops::Deref`)/[`DerefMut`](`core::ops::DerefMut`) to be
165/// maximally transparent.
166/// Some derives are automatically re-derived for the wrapper; any other traits
167/// that need to be implemented for the wrapper can be written manually.
168///
169/// #### Wrapper automatic re-derives
170// https://doc.rust-lang.org/rust-by-example/trait/derive.html
171/// [`Eq`](`core::cmp::Eq`), [`PartialEq`](`core::cmp::PartialEq`),
172/// [`Ord`](`core::cmp::Ord`), [`PartialOrd`](`core::cmp::PartialOrd`),
173/// [`Clone`](`core::clone::Clone`), [`Hash`](`core::hash::Hash`),
174/// [`Default`](`core::default::Default).
175///
176/// #### Enum Example
177/// ```
178/// # #![cfg_attr(coverage, feature(coverage_attribute))]
179/// # use std::{error::Error, fmt::{self, Debug, Display, Formatter}};
180/// use bare_err_tree::{err_tree, tree_unwrap};
181///
182/// // Generates `FooWrap<T: Debug>`
183/// #[err_tree(FooWrap)]
184/// #[derive(Debug)]
185/// enum Foo<T: Debug> {
186///     Val(T),
187///     #[dyn_err]
188///     Single(std::io::Error),
189///     #[dyn_iter_err]
190///     Many([std::io::Error; 5]),
191/// }
192///
193/// impl<T: Debug> Error for Foo<T> {
194/// #   #[cfg_attr(coverage, coverage(off))]
195///     fn source(&self) -> Option<&(dyn Error + 'static)> {
196///         # /*
197///         ...
198///         # */
199///         # unimplemented!()
200///     }
201/// }
202/// impl<T: Debug> Display for Foo<T> {
203/// #   #[cfg_attr(coverage, coverage(off))]
204///     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
205///         # /*
206///         ...
207///         # */
208///         # unimplemented!()
209///     }
210/// }
211///
212/// fn main() {
213///     let wrapped = FooWrap::from(Foo::Val(8_i32));
214///     assert!(matches!(*wrapped, Foo::Val(8_i32)));
215/// }
216///
217/// ```
218///
219/// # Full Usage Example:
220/// ```
221/// # use std::{error::Error, fmt::{self, Debug, Display, Formatter}};
222/// use bare_err_tree::{err_tree, tree_unwrap};
223///
224/// #[err_tree]
225/// #[derive(Debug)]
226/// struct Foo {
227///     #[dyn_err]
228///     io_err: std::io::Error,
229///     #[dyn_iter_err]
230///     extra_io_errs: [std::io::Error; 5],
231/// }
232///
233/// impl Foo {
234///     #[track_caller]
235///     pub fn new(io_err: std::io::Error, extra_io_errs: [std::io::Error; 5]) -> Self {
236///         Foo::_tree(io_err, extra_io_errs)
237///     }
238/// }
239///
240/// impl Error for Foo {
241///     fn source(&self) -> Option<&(dyn Error + 'static)> {
242///         Some(&self.io_err)
243///     }
244/// }
245/// impl Display for Foo {
246///     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
247///         # /*
248///         ...
249///         # */
250///         # Display::fmt(&self.io_err, f)
251///     }
252/// }
253///
254/// /// Always return the error with tree formatting support
255/// pub fn always_fail() -> Result<(), Foo> {
256///     # let get_err = || std::io::Error::from(std::io::ErrorKind::UnexpectedEof);
257///     Err(Foo::new(
258///     # /*
259///         ...
260///     # */
261///     # get_err(), std::array::from_fn(|_| get_err()),
262///     ))
263/// }
264///
265/// const MAX_DEPTH: usize = 10;
266/// const MAX_CHARS: usize = MAX_DEPTH * 6;
267///
268/// pub fn main() {
269///     # let _ = std::panic::catch_unwind(|| {
270///     let result = always_fail();
271///
272///     /// Fancy display panic with a maximum tree depth of 10 errors
273///     tree_unwrap::<MAX_CHARS, _, _>(result);
274///     # });
275/// }
276/// ```
277#[proc_macro_attribute]
278pub fn err_tree(args: TokenStream, input: TokenStream) -> TokenStream {
279    let args = parse_macro_input!(args with Punctuated::<Meta, syn::Token![,]>::parse_terminated);
280
281    let name_attribute = name_attribute(&args);
282
283    let DeriveInput {
284        attrs,
285        vis,
286        ident,
287        generics,
288        mut data,
289    } = parse_macro_input!(input as DeriveInput);
290
291    let generated = match data {
292        // Only structs are directly valid for injecting the hidden field
293        Data::Struct(ref mut data) => {
294            let errs: Vec<_> = get_struct_macros(data).collect();
295
296            if let Some(name_attribute) = name_attribute {
297                foreign_err_tree(
298                    &ident,
299                    &vis,
300                    &attrs,
301                    name_attribute,
302                    &generics,
303                    &errs,
304                    Foreign::Struct,
305                )
306            } else {
307                clean_struct_macros(data);
308                err_tree_struct(&ident, &vis, &generics, data, &errs, Foreign::Not)
309            }
310        }
311        // Enums can be handled by a generated wrapping struct
312        Data::Enum(ref mut data) => {
313            let errs: Vec<_> = get_enum_macros(data).collect();
314            clean_enum_macros(data);
315
316            if let Some(name_attribute) = name_attribute {
317                foreign_err_tree(
318                    &ident,
319                    &vis,
320                    &attrs,
321                    name_attribute,
322                    &generics,
323                    &errs,
324                    Foreign::Enum(&ident),
325                )
326            } else {
327                TokenStream::from(
328                    Error::new(
329                        Span::call_site().into(),
330                        "err_tree cannot implement directly on an enum type. Use '#[err_tree(WRAPPER)]'",
331                    )
332                    .into_compile_error(),
333                )
334            }
335        }
336        // This datatype is barely used -- mostly C interop -- so the lack of
337        // functionality doesn't really matter. I've never seen a Union Error.
338        Data::Union(_) => TokenStream::from(
339            Error::new(
340                Span::call_site().into(),
341                "err_tree cannot be annotated on union types",
342            )
343            .into_compile_error(),
344        ),
345    };
346
347    TokenStream::from_iter([
348        DeriveInput {
349            attrs,
350            vis,
351            ident,
352            generics,
353            data,
354        }
355        .into_token_stream()
356        .into(),
357        generated,
358    ])
359}
360
361#[derive(Debug)]
362enum Foreign<'a> {
363    /// Direct struct generation
364    Not,
365    /// Wrapper around a struct, doesn't need a defined ident
366    Struct,
367    /// Wrapper around an enum, needs an enum ident for pattern matching
368    Enum(&'a Ident),
369}
370
371/// Generate a foreign wrapper.
372///
373/// Boilerplates a wrapper notice into docs, copies all struct docs, creates
374/// automatic Deref and From impls, and re-derives known trivial methods.
375///
376/// Concludes with a call to [`err_tree_struct`].
377fn foreign_err_tree(
378    ident: &Ident,
379    vis: &Visibility,
380    attrs: &[Attribute],
381    name_attribute: &Ident,
382    generics: &Generics,
383    errs: &[TreeErr],
384    foreign_type: Foreign,
385) -> TokenStream {
386    let (_, ty_generics, _) = generics.split_for_impl();
387
388    let doc_attrs = attrs.iter().filter(|x| {
389        if let Ok(x) = x.meta.require_name_value() {
390            if let Some(x) = x.path.get_ident() {
391                x == "doc"
392            } else {
393                false
394            }
395        } else {
396            false
397        }
398    });
399
400    let ident_link = format!("Wrapper for [`{ident}`] generated by [`bare_err_tree`].");
401    let wrapper_struct: TokenStream = quote! {
402        #[doc = #ident_link]
403        ///
404        #(#doc_attrs)*
405        #vis struct #name_attribute #generics {
406            inner: #ident #ty_generics,
407        }
408    }
409    .into();
410
411    let mut wrapper_struct = parse_macro_input!(wrapper_struct as DeriveInput);
412
413    if let Data::Struct(ref mut wrapper_struct_data) = &mut wrapper_struct.data {
414        let boilerplate = wrapper_boilerplate(ident, generics, attrs, name_attribute);
415        let generated_impl = err_tree_struct(
416            name_attribute,
417            vis,
418            &wrapper_struct.generics,
419            wrapper_struct_data,
420            errs,
421            foreign_type,
422        );
423        TokenStream::from_iter([
424            wrapper_struct.to_token_stream().into(),
425            boilerplate,
426            generated_impl,
427        ])
428    } else {
429        panic!("The wrapper is always a struct!")
430    }
431}
432
433/// Injects `_err_tree_pkg`, the `_tree` constructor, and the `_as_err_tree`
434/// impl.
435fn err_tree_struct(
436    ident: &Ident,
437    vis: &Visibility,
438    generics: &Generics,
439    data: &mut DataStruct,
440    errs: &[TreeErr],
441    foreign: Foreign<'_>,
442) -> TokenStream {
443    let FieldsStrip {
444        bounds: field_bounds,
445        idents: field_names,
446    } = strip_fields(&data.fields);
447
448    // Generate the with_pkg call on all notated sources
449    let sources = match foreign {
450        Foreign::Not => gen_sources_struct(errs, false),
451        Foreign::Struct => gen_sources_struct(errs, true),
452        Foreign::Enum(ident) => gen_sources_enum(errs, ident),
453    };
454    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
455
456    match &mut data.fields {
457        // Struct with fields like { a: usize, b: usize }
458        Fields::Named(fields) => {
459            // Insert the pkg field
460            let field_ident = proc_macro2::Ident::new("_err_tree_pkg", Span::call_site().into());
461            fields.named.push(
462                Field::parse_named
463                    .parse2(quote! { #field_ident: ::bare_err_tree::ErrTreePkg })
464                    .unwrap(),
465            );
466            let field_ident = field_ident.into_token_stream();
467
468            quote! {
469                #[automatically_derived]
470                impl #impl_generics ::bare_err_tree::AsErrTree for #ident #ty_generics #where_clause {
471                    #[track_caller]
472                    fn as_err_tree(&self, func: &mut dyn FnMut(::bare_err_tree::ErrTree<'_>)) {
473                        let _err_tree_pkg = &self.#field_ident;
474                        #sources
475                    }
476                }
477
478                #[automatically_derived]
479                impl #impl_generics #ident #ty_generics #where_clause {
480                    #[track_caller]
481                    #[allow(clippy::too_many_arguments)]
482                    fn _tree(#field_bounds) -> Self {
483                        let #field_ident = ::bare_err_tree::ErrTreePkg::new();
484                        Self {
485                            #(#field_names,)*
486                            #field_ident
487                        }
488                    }
489                }
490            }
491            .into()
492        }
493        // Struct with fields like ( usize, usize )
494        Fields::Unnamed(fields) => {
495            // Insert the pkg field
496            let prev_len = syn::Index::from(fields.unnamed.len());
497            fields.unnamed.push(
498                Field::parse_unnamed
499                    .parse2(quote! { ::bare_err_tree::ErrTreePkg })
500                    .unwrap(),
501            );
502
503            quote! {
504                #[automatically_derived]
505                impl #impl_generics ::bare_err_tree::AsErrTree for #ident #ty_generics #where_clause {
506                    #[track_caller]
507                    fn as_err_tree(&self, func: &mut dyn FnMut(::bare_err_tree::ErrTree<'_>)) {
508                        let _err_tree_pkg = &self.#prev_len;
509                        #sources
510                    }
511                }
512
513                #[automatically_derived]
514                impl #impl_generics #ident #ty_generics #where_clause {
515                    #[track_caller]
516                    #[allow(clippy::too_many_arguments)]
517                    fn _tree(#field_bounds) -> Self {
518                        let _err_tree_pkg = ::bare_err_tree::ErrTreePkg::new();
519                        Self (
520                            #(#field_names,)*
521                            _err_tree_pkg
522                        )
523                    }
524                }
525            }
526            .into()
527        }
528        // Transmutes a unit struct into a named struct for pkg injection
529        // Adds new and default methods for easy construction
530        Fields::Unit => {
531            // Insert the pkg field
532            let field_ident = proc_macro2::Ident::new("_err_tree_pkg", Span::call_site().into());
533            let mut named = Punctuated::default();
534            named.push(
535                Field::parse_named
536                    .parse2(quote! { #field_ident: ::bare_err_tree::ErrTreePkg })
537                    .unwrap(),
538            );
539            let field_ident = field_ident.into_token_stream();
540            data.fields = Fields::Named(FieldsNamed {
541                brace_token: Brace::default(),
542                named,
543            });
544
545            quote! {
546                #[automatically_derived]
547                impl #impl_generics ::bare_err_tree::AsErrTree for #ident #ty_generics #where_clause {
548                    #[track_caller]
549                    fn as_err_tree(&self, func: &mut dyn FnMut(::bare_err_tree::ErrTree<'_>)) {
550                        let _err_tree_pkg = &self.#field_ident;
551                        #sources
552                    }
553                }
554
555                #[automatically_derived]
556                impl #impl_generics #ident #ty_generics #where_clause {
557                    #[track_caller]
558                    fn _tree() -> Self {
559                        let #field_ident = ::bare_err_tree::ErrTreePkg::new();
560                        Self {
561                            #field_ident
562                        }
563                    }
564                }
565
566                #[automatically_derived]
567                impl #impl_generics ::core::default::Default for #ident #ty_generics #where_clause {
568                    #[track_caller]
569                    fn default() -> Self {
570                        Self::_tree()
571                    }
572                }
573
574                #[automatically_derived]
575                impl #impl_generics #ident #ty_generics #where_clause {
576                    #[track_caller]
577                    #vis fn new() -> Self {
578                        Self::_tree()
579                    }
580                }
581            }
582            .into()
583        }
584    }
585}