docsplay_macros/lib.rs
1//! Derive macro implementation
2#![doc(html_root_url = "https://docs.rs/docsplay/0.1.0")]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![warn(
5 rust_2018_idioms,
6 unreachable_pub,
7 bad_style,
8 dead_code,
9 improper_ctypes,
10 non_shorthand_field_patterns,
11 no_mangle_generic_items,
12 overflowing_literals,
13 path_statements,
14 patterns_in_fns_without_body,
15 unconditional_recursion,
16 unused,
17 unused_allocation,
18 unused_comparisons,
19 unused_parens,
20 while_true
21)]
22#![allow(clippy::try_err)]
23
24#[allow(unused_extern_crates)]
25extern crate proc_macro;
26
27mod attr;
28mod expand;
29mod fmt;
30
31use proc_macro::TokenStream;
32use syn::{parse_macro_input, DeriveInput};
33
34/// [Custom `#[derive(...)]` macro](https://doc.rust-lang.org/edition-guide/rust-2018/macros/custom-derive.html)
35/// for implementing [`fmt::Display`][core::fmt::Debug] via doc comment attributes.
36///
37/// ## Generic Type Parameters
38///
39/// Type parameters to an enum or struct using this macro should *not* need to
40/// have an explicit `Display` constraint at the struct or enum definition
41/// site. A `Display` implementation for the `derive`d struct or enum is
42/// generated assuming each type parameter implements `Display`, but that should
43/// be possible without adding the constraint to the struct definition itself:
44///
45/// ```rust,ignore
46/// use docsplay::Display;
47///
48/// /// oh no, an error: {0}
49/// #[derive(Display)]
50/// pub struct Error<E>(pub E);
51///
52/// // No need to require `E: Display`, since `docsplay::Display` adds that implicitly.
53/// fn generate_error<E>(e: E) -> Error<E> { Error(e) }
54///
55/// assert!("oh no, an error: muahaha" == &format!("{}", generate_error("muahaha")));
56/// ```
57///
58/// ## Using [`Debug`][core::fmt::Debug] Implementations with Type Parameters
59/// However, if a type parameter must instead be constrained with the
60/// [`Debug`][core::fmt::Debug] trait so that some field may be printed with
61/// `{:?}`, that constraint must currently still also be specified redundantly
62/// at the struct or enum definition site. If a struct or enum field is being
63/// formatted with `{:?}` via `docsplay`, and a generic type
64/// parameter must implement `Debug` to do that, then that struct or enum
65/// definition will need to propagate the `Debug` constraint to every type
66/// parameter it's instantiated with:
67///
68/// ```rust,ignore
69/// use core::fmt::Debug;
70/// use docsplay::Display;
71///
72/// /// oh no, an error: {0:?}
73/// #[derive(Display)]
74/// pub struct Error<E: Debug>(pub E);
75///
76/// // `E: Debug` now has to propagate to callers.
77/// fn generate_error<E: Debug>(e: E) -> Error<E> { Error(e) }
78///
79/// assert!("oh no, an error: \"cool\"" == &format!("{}", generate_error("cool")));
80///
81/// // Try this with a struct that doesn't impl `Display` at all, unlike `str`.
82/// #[derive(Debug)]
83/// pub struct Oh;
84/// assert!("oh no, an error: Oh" == &format!("{}", generate_error(Oh)));
85/// ```
86#[proc_macro_derive(
87 Display,
88 attributes(ignore_extra_doc_attributes, prefix_enum_doc_attributes, display)
89)]
90pub fn derive_error(input: TokenStream) -> TokenStream {
91 let input = parse_macro_input!(input as DeriveInput);
92 expand::derive(&input)
93 .unwrap_or_else(|err| err.to_compile_error())
94 .into()
95}