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}