manyhow/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![warn(clippy::pedantic, missing_docs)]
3#![allow(clippy::module_name_repetitions)]
4//! Proc **m**acro **anyhow**, a combination of ideas from
5//! [`anyhow`](docs.rs/anyhow) and
6//! [`proc-macro-error`](docs.rs/proc-macro-error) to improve proc macro
7//! development, especially focused on the error handling.
8//!
9//! # Motivation
10//! Error handling in proc-macros is unideal, as the top level functions of
11//! proc-macros can only return `TokenStreams` both in success and failure case.
12//! This means that I often write code like this, moving the actual
13//! implementation in a separate function to be able to use the ergonomic rust
14//! error handling with e.g., `?`.
15//! ```
16//! # use proc_macro2::TokenStream;
17//! # use quote::quote;
18//! # use syn2 as syn;
19//! use proc_macro2::TokenStream as TokenStream2;
20//!
21//! # let _ = quote!{
22//! #[proc_macro]
23//! # };
24//! pub fn my_macro(input: TokenStream) -> TokenStream {
25//!     match actual_implementation(input.into()) {
26//!         Ok(output) => output,
27//!         Err(error) => error.into_compile_error(),
28//!     }
29//!     .into()
30//! }
31//!
32//! fn actual_implementation(input: TokenStream2) -> syn::Result<TokenStream2> {
33//!     // ..
34//! #   Ok(quote!())
35//! }
36//! ```
37//!
38//! # Using the `#[manyhow]` macro
39//! To activate the error handling, just add [`#[manyhow]`](manyhow) above any
40//! proc-macro implementation, reducing the above example to:
41//!
42//! ```
43//! # use quote::quote;
44//! # use syn2 as syn;
45//! use manyhow::manyhow;
46//! use proc_macro2::TokenStream as TokenStream2;
47//!
48//! # let _ = quote!{
49//! #[manyhow]
50//! #[proc_macro]
51//! # };
52//! // You can also merge the two attributes: #[manyhow(proc_macro)]
53//! fn my_macro(input: TokenStream2) -> syn::Result<TokenStream2> {
54//!     // ..
55//! #   Ok(quote!())
56//! }
57//!
58//! // On top of the TokenStreams any type that implements [`Parse`] is supported
59//! # let _ = quote!{
60//! #[manyhow(proc_macro_derive(MyMacro))]
61//! #[proc_macro]
62//! # };
63//! // The output can also be anything that implements `quote::ToTokens`
64//! fn my_derive_macro(input: syn::DeriveInput) -> manyhow::Result<syn::ItemImpl> {
65//!     // ..
66//! #   manyhow::bail!("hello")
67//! }
68//! ```
69//!
70//! See [Without macros](#without-macros) to see what this expands to under the
71//! hood.
72//!
73//! You can also use the `#[manyhow]` attributes on a use statement, useful when
74//! moving your proc-macro implementations in separate modules.
75//!
76//! ```
77//! # use quote::quote;
78//! use manyhow::manyhow;
79//!
80//! mod module {
81//!     # use quote::quote;
82//!     # use syn2 as syn;
83//!     use proc_macro2::TokenStream as TokenStream2;
84//!
85//!     pub fn my_macro(input: TokenStream2) -> syn::Result<TokenStream2> {
86//!         // ..
87//!     #   Ok(quote!())
88//!     }
89//! }
90//!
91//! # let _ = quote!{
92//! #[manyhow]
93//! #[proc_macro]
94//! # };
95//! // You can also merge the two attributes: #[manyhow(proc_macro)]
96//! pub use module::my_macro;
97//! ```
98//!
99//! A proc macro function marked as `#[manyhow]` can take and return any
100//! [`TokenStream`](AnyTokenStream), and can also return `Result<TokenStream,
101//! E>` where `E` implements [`ToTokensError`]. As additional parameters a
102//! [dummy](#dummy-mut-tokenstream) and/or [emitter](#emitter-mut-emitter) can
103//! be specified.
104//!
105//! The `manyhow` attribute takes optional flags to configure its behavior.
106//!
107//! When used for `proc_macro` and `proc_macro_attribute`,
108//! `#[manyhow(input_as_dummy, ...)]` will take the input of a function like
109//! `proc_macro` to initialize the [dummy `&mut TokenStream`](#
110//! dummy-mut-tokenstream) while `#[manyhow(item_as_dummy, ...)]` on
111//! `proc_macro_attribute` will initialize the dummy with the annotated item.
112//!
113//! You can merge the `#[proc_macro*]` attribute inside the manyhow flags e.g.,
114//! `#[manyhow(proc_macro)]` or `#[manyhow(proc_macro_derive(SomeTrait, ...))]`.
115//!
116//! The `#[manyhow(impl_fn, ...)]` flag will put the actual macro implementation
117//! in a separate function. Making it available for e.g., unit testing with
118//! [`proc_macro_utils::assert_expansion!`](https://docs.rs/proc-macro-utils/latest/proc_macro_utils/macro.assert_expansion.html).
119//!
120//! ```ignore
121//! #[manyhow(impl_fn)]
122//! #[proc_macro]
123//! pub fn actual_macro(input: TokenStream2) -> TokenStream2 {
124//!     // ...
125//! }
126//! // would roughly expand to
127//! #[proc_macro]
128//! pub fn actual_macro(input: TokenStream) -> TokenStream {
129//!     actual_macro_impl(input.into()).into()
130//! }
131//! fn actual_macro_impl(input: TokenStream2) -> TokenStream2 {
132//!     // ...
133//! }
134//! ```
135//!
136//! # Without macros
137//! `manyhow` can be used without proc macros, and they can be disabled by
138//! adding `manyhow` with `default-features=false`.
139//!
140//! The usage is more or less the same, though with some added boilerplate from
141//! needing to invoke one of [`function()`] ([`function!`]), [`attribute()`]
142//! ([`attribute!`]) or [`derive()`] ([`derive!`]) directly. For each version
143//! there exists a function and a `macro_rules` macro, while the function only
144//! supports [`proc_macro::TokenStream`] and [`proc_macro2::TokenStream`], the
145//! macro versions also support any type that implements [`Parse`]
146//! and [`ToTokens`] respectively.
147//!
148//! While the examples use closures, functions can be passed in as well. The
149//! above example would then change to:
150//! ```
151//! # use proc_macro2::TokenStream;
152//! # use quote::quote;
153//! # use syn2 as syn;
154//! use proc_macro2::TokenStream as TokenStream2;
155//!
156//! # let _ = quote!{
157//! #[proc_macro]
158//! # };
159//! pub fn my_macro(input: TokenStream) -> TokenStream {
160//! # let tmp = input.clone();
161//! # let output: TokenStream =
162//!     manyhow::function(
163//!         input,
164//!         false,
165//!         |input: TokenStream2| -> syn::Result<TokenStream2> {
166//!             // ..
167//! #           Ok(quote!())
168//!         },
169//!     )
170//! # ;
171//! # let input = tmp;
172//!     // Or
173//!     manyhow::function!(
174//!         input,
175//!         |input: syn::DeriveInput| -> manyhow::Result<syn::ItemImpl> {
176//!             // ..
177//! #           manyhow::bail!("error")
178//!         },
179//!     )
180//! }
181//! ```
182//! [`Emitter`](#emitter-mut-emitter) and [dummy
183//! `TokenStream`](#dummy-mut-tokenstream) can also be used. [`function()`]
184//! ([`function!`]) and [`attribute()`] ([`attribute!`]) take an additional
185//! boolean parameter controlling whether the input/item will be used as initial
186//! dummy.
187//!
188//! # `emitter: &mut Emitter`
189//! [`*MacroHandler`](FunctionMacroHandler)s (the traits defining what
190//! closures/functions can be used with `manyhow`) can take a mutable reference
191//! to an [`Emitter`]. This allows collecting errors, but not fail immediately.
192//!
193//! [`Emitter::into_result`] can be used to return if an [`Emitter`] contains
194//! any values.
195//!
196//! ```
197//! # use quote::quote;
198//! # use syn2 as syn;
199//! use manyhow::{manyhow, Emitter, ErrorMessage};
200//! use proc_macro2::TokenStream as TokenStream2;
201//!
202//! # let _ = quote!{
203//! #[manyhow]
204//! #[proc_macro]
205//! # };
206//! fn my_macro(input: TokenStream2, emitter: &mut Emitter) -> manyhow::Result<TokenStream2> {
207//!     // ..
208//!     emitter.emit(ErrorMessage::call_site("A fun error!"));
209//!     emitter.into_result()?;
210//!     // ..
211//! #   Ok(quote!())
212//! }
213//! ```
214//!
215//! # `dummy: &mut TokenStream`
216//! [`*MacroHandler`](FunctionMacroHandler)s can also take a mutable reference
217//! to a `TokenStream`, to enable emitting some dummy code to be used in case
218//! the macro errors.
219//!
220//! This allows either appending tokens e.g., with [`ToTokens::to_tokens`] or
221//! directly setting the dummy code e.g., `*dummy = quote!{some tokens}`.
222//!
223//! # Crate features
224//!
225//! - `macros` **default** Enables [`#[manyhow]`](macros::manyhow) attribute
226//!   macro.
227//! - `syn`/`syn2` **default** Enables errors for [`syn` 2.x](https://docs.rs/syn/latest/syn/).
228//! - `syn1` Enables errors for [`syn` 1.x](https://docs.rs/syn/1.0.109/syn/index.html).
229//! - `darling` Enables errors for [`darling`](https://docs.rs/darling/latest/index.html).
230
231#[cfg(feature = "macros")]
232pub use macros::manyhow;
233use proc_macro2::TokenStream;
234#[cfg(doc)]
235use {quote::ToTokens, syn2::parse::Parse};
236
237extern crate proc_macro;
238
239#[macro_use]
240mod span_ranged;
241pub use span_ranged::{to_tokens_span_range, SpanRanged};
242#[macro_use]
243mod macro_rules;
244mod error;
245pub use error::*;
246
247mod parse_to_tokens;
248
249#[doc(hidden)]
250pub mod __private {
251    pub use std::prelude::rust_2021::*;
252
253    use proc_macro2::TokenStream;
254    pub use quote;
255
256    pub use crate::span_ranged::*;
257    pub type Dummy = Option<TokenStream>;
258
259    pub use crate::parse_to_tokens::*;
260}
261
262/// Marker trait for [`proc_macro::TokenStream`] and
263/// [`proc_macro2::TokenStream`]
264pub trait AnyTokenStream: Clone + From<TokenStream> + Into<TokenStream> + Default {}
265impl AnyTokenStream for TokenStream {}
266impl AnyTokenStream for proc_macro::TokenStream {}
267
268#[macro_export]
269#[doc(hidden)]
270macro_rules! __macro_handler {
271    ($name:ident; $($(#attr=$attr:tt)? $n:ident: $input:expr),+; $impl:expr$(; dummy:$dummy:expr)?) => {
272        $crate::__macro_handler! {! $name; $($(#attr=$attr)? $n: $input.clone()),+; $impl $(; $crate::__private::Some($dummy))?}
273    };
274    ($name:ident; $($(#attr=$attr:tt)? $n:ident: $input:expr),+; $impl:expr; dummy) => {
275        $crate::__macro_handler! {! $name; $($(#attr=$attr)? $n: $input),+; $impl; $crate::__private::Dummy::None}
276    };
277    (! $name:ident; $($(#attr=$attr:tt)? $n:ident: $input:expr),+; $impl:expr $(; $dummy:expr)?) => {{
278        use $crate::__private::{ManyhowParse, ManyhowToTokens, ManyhowTry};
279        let implementation = $impl;
280        $(let $n = &$crate::__private::WhatType::new();)+
281        if false {
282            _ = $crate::__private::$name($($n.identify(),)+ $($dummy,)? implementation);
283            unreachable!();
284        } else {
285            match $crate::__private::$name($(
286                {#[allow(unused)]
287                let attr = false;
288                $(let attr = $attr;)?
289                $n.manyhow_parse($input, attr)},
290            )+ $($dummy,)? implementation)
291            {
292                Err(tokens) => tokens.into(),
293                Ok((output, mut tokens, mut dummy)) => {
294                    match (&$crate::__private::WhatType::from(&output)).manyhow_try(output) {
295                        Err(error) => {
296                            dummy.extend(tokens);
297                            tokens = dummy;
298                            (&$crate::__private::WhatType::from(&error)).manyhow_to_tokens(error, &mut tokens);
299                        },
300                        Ok(output) => (&$crate::__private::WhatType::from(&output)).manyhow_to_tokens(output, &mut tokens),
301                    };
302                    tokens.into()
303                }
304            }
305        }
306    }};
307}
308
309/// Handles [`proc_macro_attribute`](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
310/// implementation
311///
312/// Takes any `TokenStream` for `input` and `item` and returns any
313/// `TokenStream`. If `item_as_dummy = true` the item input will be used as
314/// default dummy code on error. `body` takes a [`AttributeMacroHandler`] with
315/// two `TokenStream` parameters. And an optional [`&mut Emitter`](Emitter) and
316/// a `&mut TokenStream` for storing a dummy output.
317///
318/// ```
319/// # use proc_macro_utils::assert_tokens;
320/// # use quote::{quote, ToTokens};
321/// use manyhow::{attribute, Emitter, Result};
322/// use proc_macro2::TokenStream;
323/// # let input = quote!();
324/// # let item = quote!();
325/// # let output: TokenStream =
326/// attribute(
327///     input,
328///     item,
329///     false,
330///     |input: TokenStream,
331///      item: TokenStream,
332///      dummy: &mut TokenStream,
333///      emitter: &mut Emitter|
334///      -> Result {
335///         // ..
336///         # Ok(quote!())
337///     },
338/// );
339/// ```
340///
341/// *Note:* When `item_as_dummy = true` the `dummy: &mut TokenStream` will be
342/// initialized with `item`. To override assign a new `TokenStream`:
343/// ```
344/// # use proc_macro_utils::assert_tokens;
345/// use manyhow::{attribute, Result, SilentError};
346/// use proc_macro2::TokenStream;
347/// use quote::{quote, ToTokens};
348/// # let input = quote!(input);
349/// let item = quote!(
350///     struct Struct;
351/// );
352/// let output: TokenStream = attribute(
353///     input,
354///     item,
355///     true,
356///     |input: TokenStream,
357///      item: TokenStream,
358///      dummy: &mut TokenStream|
359///      -> Result<TokenStream, SilentError> {
360///         assert_tokens!(dummy.to_token_stream(), {
361///             struct Struct;
362///         });
363///         *dummy = quote! {
364///             struct Struct(HelloWorld);
365///         };
366///         // ..
367///         Err(SilentError)
368///     },
369/// );
370///
371/// assert_tokens! {output, {struct Struct(HelloWorld);}};
372/// ```
373pub fn attribute<
374    Input: AnyTokenStream,
375    Item: AnyTokenStream,
376    Dummy: AnyTokenStream,
377    Output: MacroOutput,
378    Return: AnyTokenStream,
379    Function,
380>(
381    input: impl AnyTokenStream,
382    item: impl AnyTokenStream,
383    item_as_dummy: bool,
384    body: impl AttributeMacroHandler<
385        Function,
386        Item = Item,
387        Input = Input,
388        Dummy = Dummy,
389        Output = Output,
390    >,
391) -> Return {
392    #[allow(unused_mut)]
393    let mut tokens = Dummy::default();
394    let mut tokens = if item_as_dummy {
395        item.clone().into().into()
396    } else {
397        tokens
398    };
399    let mut emitter = Emitter::new();
400    let output = body.call(
401        input.into().into(),
402        item.into().into(),
403        &mut tokens,
404        &mut emitter,
405    );
406    let mut tokens = tokens.into();
407    let mut tokens = match output.convert() {
408        Ok(tokens) => tokens,
409        Err(error) => {
410            error.to_tokens(&mut tokens);
411            tokens
412        }
413    };
414    emitter.to_tokens(&mut tokens);
415    tokens.into()
416}
417
418/// Handles [`proc_macro_attribute`](https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros)
419/// implementation
420///
421/// Takes any `TokenStream` for `input` and `item` and its return value. If
422/// `#[as_dummy]` is specified on item, it will be used as default
423/// dummy code on error. `body` takes a [`AttributeMacroHandler`] with two
424/// `TokenStream`s or types implementing [`Parse`] parameters and returning a
425/// `TokenStream` or type implementing [`ToTokens`]. And an optional [`&mut
426/// Emitter`](Emitter) and a `&mut TokenStream` for storing a dummy output.
427///
428///
429/// ```
430/// # use proc_macro_utils::assert_tokens;
431/// # use quote::{quote, ToTokens};
432/// use manyhow::{attribute, Emitter, Result};
433/// use proc_macro2::TokenStream;
434/// # let input = quote!();
435/// # let item = quote!();
436/// # let output: TokenStream =
437/// attribute!(input, item, |input: TokenStream,
438///                          item: TokenStream,
439///                          dummy: &mut TokenStream,
440///                          emitter: &mut Emitter|
441///  -> Result {
442///     // ..
443///         # Ok(quote!())
444/// });
445/// ```
446///
447/// *Note:* When `#[as_dummy]` is specified the `dummy: &mut TokenStream` will
448/// be initialized with `item`. To override assign a new `TokenStream`:
449/// ```
450/// # use proc_macro_utils::assert_tokens;
451/// # use syn2 as syn;
452/// use manyhow::{attribute, Result, SilentError};
453/// use proc_macro2::TokenStream;
454/// use quote::{quote, ToTokens};
455/// # let input = quote!(input);
456/// let item = quote!(
457///     struct Struct;
458/// );
459/// let output: TokenStream = attribute!(
460///     input,
461///     #[as_dummy]
462///     item,
463///     |input: TokenStream,
464///      item: syn::ItemStruct,
465///      dummy: &mut TokenStream|
466///      -> Result<syn::ItemStruct, SilentError> {
467///         assert_tokens!(dummy.to_token_stream(), {
468///             struct Struct;
469///         });
470///         *dummy = quote! {
471///             struct Struct(HelloWorld);
472///         };
473///         // ..
474///         Err(SilentError)
475///     },
476/// );
477///
478/// assert_tokens! {output, {struct Struct(HelloWorld);}};
479/// ```
480#[macro_export]
481macro_rules! attribute {
482    ($input:expr, #[as_dummy] $item:expr, $impl:expr $(,)?) => {
483        $crate::__macro_handler!{attribute_transparent; #attr=true input: $input, item: $item.clone(); $impl; dummy: $item}
484    };
485    ($input:expr, $item:expr, $impl:expr $(,)?) => {
486        $crate::__macro_handler!{attribute_transparent; #attr=true input: $input, item: $item; $impl; dummy}
487    };
488}
489
490/// Handles [`proc_macro_derive`](https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros)
491/// implementation.
492///
493/// Use [`derive!`] to support [`Parse`] and [`ToTokens`] as well.
494///
495/// Takes any `TokenStream` for `item` and returns any `TokenStream`. `body`
496/// takes a [`DeriveMacroHandler`] with one `TokenStream` parameter. And an
497/// optional [`&mut Emitter`](Emitter) and `&mut TokenStream` for storing a
498/// dummy output.
499///
500/// ```
501/// # use proc_macro_utils::assert_tokens;
502/// # use quote::{quote, ToTokens};
503/// use manyhow::{derive, Emitter, Result};
504/// use proc_macro2::TokenStream;
505/// # let item = quote!();
506/// # let output: TokenStream =
507/// derive(
508///     item,
509///     |item: TokenStream, dummy: &mut TokenStream, emitter: &mut Emitter| -> Result {
510///         // ..
511///         # Ok(quote!())
512///     },
513/// );
514/// ```
515pub fn derive<
516    Item: AnyTokenStream,
517    Dummy: AnyTokenStream,
518    Output: MacroOutput,
519    Return: AnyTokenStream,
520    Function,
521>(
522    item: impl AnyTokenStream,
523    body: impl DeriveMacroHandler<Function, Item = Item, Dummy = Dummy, Output = Output>,
524) -> Return {
525    let mut tokens = Dummy::default();
526    let mut emitter = Emitter::new();
527    let output = body.call(item.into().into(), &mut tokens, &mut emitter);
528    let mut tokens = tokens.into();
529    let mut tokens = match output.convert() {
530        Ok(tokens) => tokens,
531        Err(error) => {
532            error.to_tokens(&mut tokens);
533            tokens
534        }
535    };
536    emitter.to_tokens(&mut tokens);
537    tokens.into()
538}
539
540/// Handles [`proc_macro_derive`](https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros)
541/// implementation.
542///
543/// Takes any `TokenStream` for `item` and returns any `TokenStream`. `body`
544/// takes a [`DeriveMacroHandler`] with one `TokenStream` or type implementing
545/// [`Parse`] parameter and returns a `TokenStream` or type implementing
546/// [`ToTokens`]. And an optional [`&mut Emitter`](Emitter) and `&mut
547/// TokenStream` for storing a dummy output.
548///
549/// ```
550/// # use proc_macro_utils::assert_tokens;
551/// # use quote::{quote, ToTokens};
552/// # use syn2 as syn;
553/// use manyhow::{derive, Emitter, Result};
554/// use proc_macro2::TokenStream;
555/// # let item = quote!();
556/// # let output: TokenStream =
557/// derive!(item, |item: syn::DeriveInput,
558///                dummy: &mut TokenStream,
559///                emitter: &mut Emitter|
560///  -> Result {
561///     // ..
562///         # Ok(quote!())
563/// });
564/// ```
565#[macro_export]
566macro_rules! derive {
567    ($item:expr, $impl:expr $(,)?) => {
568        $crate::__macro_handler! {derive_transparent; item: $item; $impl}
569    };
570}
571
572/// Handles function like [`proc_macro`](https://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros)
573/// implementation
574///
575/// Use [`function!`] to support [`Parse`] and [`ToTokens`] as well.
576///
577/// Takes any `TokenStream` for `input` and returns any
578/// `TokenStream`. If `input_as_dummy = true` the item input will be used as
579/// default dummy code on error. `body` takes a [`FunctionMacroHandler`] with
580/// one `TokenStream` parameter. And an optional [`&mut Emitter`](Emitter) and a
581/// `&mut TokenStream` for storing a dummy output.
582///
583/// ```
584/// # use proc_macro_utils::assert_tokens;
585/// # use quote::{quote, ToTokens};
586/// use manyhow::{function, Emitter, Result};
587/// use proc_macro2::TokenStream;
588/// # let input = quote!();
589/// # let output: TokenStream =
590/// function(
591///     input,
592///     false,
593///     |input: TokenStream, dummy: &mut TokenStream, emitter: &mut Emitter| -> Result {
594///         // ..
595///         # Ok(quote!())
596///     },
597/// );
598/// ```
599///
600/// *Note:* When `input_as_dummy = true` the `dummy: &mut TokenStream` will be
601/// initialized with `input`. To override assign a new `TokenStream`:
602/// ```
603/// # use proc_macro_utils::assert_tokens;
604/// use manyhow::{function, Result, SilentError};
605/// use proc_macro2::TokenStream;
606/// use quote::{quote, ToTokens};
607/// let input = quote!(some input);
608/// let output: TokenStream = function(
609///     input,
610///     true,
611///     |input: TokenStream,
612///      dummy: &mut TokenStream|
613///      -> Result<TokenStream, SilentError> {
614///         assert_tokens!(dummy.to_token_stream(), {
615///             some input
616///         });
617///         *dummy = quote! {
618///             another input
619///         };
620///         // ..
621///         Err(SilentError)
622///     },
623/// );
624///
625/// assert_tokens! {output, {another input}};
626/// ```
627pub fn function<
628    Input: AnyTokenStream,
629    Dummy: AnyTokenStream,
630    Output: MacroOutput,
631    Return: AnyTokenStream,
632    Function,
633>(
634    input: impl AnyTokenStream,
635    input_as_dummy: bool,
636    body: impl FunctionMacroHandler<Function, Input = Input, Dummy = Dummy, Output = Output>,
637) -> Return {
638    let mut tokens = if input_as_dummy {
639        input.clone().into().into()
640    } else {
641        Dummy::default()
642    };
643    let mut emitter = Emitter::new();
644    let output = body.call(input.into().into(), &mut tokens, &mut emitter);
645    let mut tokens = tokens.into();
646    let mut tokens = match output.convert() {
647        Ok(tokens) => tokens,
648        Err(error) => {
649            error.to_tokens(&mut tokens);
650            tokens
651        }
652    };
653    emitter.to_tokens(&mut tokens);
654    tokens.into()
655}
656
657/// Handles function like [`proc_macro`](https://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros)
658/// implementation
659///
660/// Takes any `TokenStream` for `input` and returns any `TokenStream`. If
661/// `#[as_dummy]` is specified on input, it will be used as default
662/// dummy code on error. `body` takes a [`FunctionMacroHandler`] with one
663/// `TokenStream` or type implementing [`Parse`] parameter and returns a
664/// `TokenStream` or type implementing [`ToTokens`]. And an optional [`&mut
665/// Emitter`](Emitter) and a `&mut TokenStream` for storing a dummy output.
666///
667/// ```
668/// # use proc_macro_utils::assert_tokens;
669/// # use quote::{quote, ToTokens};
670/// # use syn2 as syn;
671/// use manyhow::{function, Emitter, Result};
672/// use proc_macro2::TokenStream;
673/// # let input = quote!();
674/// # let output: TokenStream =
675/// function!(input, |input: syn::Item,
676///                   dummy: &mut TokenStream,
677///                   emitter: &mut Emitter|
678///  -> Result<syn::ItemImpl> {
679///     // ..
680///         # manyhow::bail!("unimplemented")
681/// });
682/// ```
683///
684/// *Note:* When `#[as_dummy]` is specified on the input, the `dummy: &mut
685/// TokenStream` will be initialized with `input`. To override assign a new
686/// `TokenStream`:
687///
688/// ```
689/// use proc_macro_utils::assert_tokens;
690/// use manyhow::{function, Result, SilentError};
691/// use proc_macro2::TokenStream;
692/// use quote::{quote, ToTokens};
693///
694/// let input = quote!(some input);
695/// let output: TokenStream = function!(
696///     #[as_dummy] input,
697///     |input: TokenStream, dummy: &mut TokenStream|
698///      -> Result<TokenStream, SilentError> {
699///         assert_tokens!(dummy.to_token_stream(), {
700///             some input
701///         });
702///         *dummy = quote! {
703///             another input
704///         };
705///         // ..
706///         Err(SilentError)
707///     },
708/// );
709///
710/// assert_tokens! {output, {another input}};
711/// ```
712#[macro_export]
713macro_rules! function {
714    (#[as_dummy] $input:expr, $impl:expr $(,)?) => {
715        $crate::__macro_handler! {function_transparent; input: $input; $impl; dummy: $input}
716    };
717    ($input:expr, $impl:expr $(,)?) => {
718        $crate::__macro_handler! {function_transparent; input: $input; $impl; dummy}
719    };
720}
721
722#[test]
723fn function_macro() {
724    use proc_macro::TokenStream as TokenStream1;
725    use quote::quote;
726    // proc_macro2::TokenStream
727    let output: TokenStream =
728        function!(quote!(hello), |input: TokenStream| -> TokenStream { input });
729    assert_eq!(output.to_string(), "hello");
730    // proc_macro::TokenStream do not run :D
731    if false {
732        let _: TokenStream1 = function!(
733            TokenStream1::from(quote!(hello)),
734            |input: TokenStream1| -> TokenStream1 { input }
735        );
736    }
737
738    #[cfg(feature = "syn2")]
739    {
740        use quote::ToTokens;
741        let output: TokenStream = function!(
742            #[as_dummy]
743            quote!(hello;),
744            |input: syn2::LitInt| -> TokenStream { input.into_token_stream() }
745        );
746        assert_eq!(
747            output.to_string(),
748            quote!(hello; ::core::compile_error! { "expected integer literal" }).to_string()
749        );
750        let output: TokenStream = function!(quote!(20), |_input: syn2::LitInt| -> syn2::Ident {
751            syn2::parse_quote!(hello)
752        });
753        assert_eq!(output.to_string(), "hello");
754    }
755}
756
757macro_rules! macro_input {
758    ($MacroInput:ident; $($input:ident: $Input:ident),+; $a:literal; $name:literal; $token_streams:literal) => {
759        /// Input of
760        #[doc = $a]
761        #[doc = $name]
762        /// proc-macro
763        ///
764        /// Note: for `TokenStream` either [`proc_macro::TokenStream`] or
765        /// [`proc_macro2::TokenStream`] can be used.
766        ///
767        /// Trait is implemented for any [`function`](FnOnce), taking in
768        #[doc = concat!($token_streams, ".")]
769        /// Additionally, they can take optionally in any order a [`&mut
770        /// Emitter`](Emitter) which allows emitting errors without returning early. And
771        /// a `&mut TokenStream` to return a dummy `TokenStream` on failure.
772        ///
773        /// When used with
774        #[doc = concat!("[`", $name, "()`]")]
775        /// it must return a type implementing [`MacroOutput`], with
776        #[doc = concat!("[`", $name, "!`]")]
777        /// it can accept types implementing [`Parse`] and return types
778        /// implementing [`ToTokens`](quote::ToTokens).
779        #[allow(missing_docs)]
780        pub trait $MacroInput<Function> {
781            $(type $Input;)+
782            type Dummy;
783            type Output;
784            #[allow(clippy::missing_errors_doc)]
785            fn call(
786                self,
787                $(item: Self::$Input,)+
788                dummy: &mut Self::Dummy,
789                emitter: &mut Emitter,
790            ) -> Self::Output;
791        }
792
793        macro_input_impl!([$($Input,)+ Dummy: Clone]; $MacroInput; $($input: $Input),*; &mut Dummy, &mut Emitter; dummy: Dummy dummy; emitter emitter);
794        macro_input_impl!([$($Input,)+ Dummy: Clone]; $MacroInput; $($input: $Input),*; &mut Dummy; dummy: Dummy dummy; _emitter);
795        macro_input_impl!([$($Input),+]; $MacroInput; $($input: $Input),*; &mut Emitter; _dummy: TokenStream; emitter emitter);
796        macro_input_impl!([$($Input),+]; $MacroInput; $($input: $Input),*; ; _dummy: TokenStream; _emitter);
797    };
798}
799
800macro_rules! macro_input_impl {
801    ([$($gen:tt)*]; $MacroInput:ident; $($input:ident: $Input:ident),+; $($Extra:ty),*; $dummy1:ident: $Dummy:ident $($dummy2:ident)?; $emitter1:ident $($emitter2:ident)?) => {
802        impl<$($gen)*, Output, Function> $MacroInput<($($Input,)+ $($Extra,)* Output)> for Function
803        where
804            Function: Fn($($Input,)+ $($Extra,)*) -> Output,
805        {
806            type Dummy = $Dummy;
807            $(type $Input = $Input;)*
808            type Output = Output;
809
810            fn call(
811                self,
812                $($input: Self::$Input,)*
813                $dummy1: &mut Self::Dummy,
814                $emitter1: &mut Emitter,
815            ) -> Self::Output {
816                self($($input,)+ $($dummy2,)? $($emitter2,)?)
817            }
818        }
819
820    }
821}
822
823macro_input!(FunctionMacroHandler; input: Input; "a"; "function"; "one `TokenStream`");
824macro_input!(DeriveMacroHandler; item: Item; "a"; "derive"; "one `TokenStream`");
825macro_input!(AttributeMacroHandler; input: Input, item: Item; "an"; "attribute"; "two `TokenStream`s");
826
827#[rustfmt::skip]
828#[allow(clippy::doc_markdown)]
829/// Output of a macro handler.
830///
831/// Enables support for returning any [`TokenStream`](AnyTokenStream) or
832/// <code>[Result]<[TokenStream](AnyTokenStream), [impl ToTokensError](ToTokensError)></code>
833/// from a proc-macro implementation.
834pub trait MacroOutput {
835    /// Handles conversion into a <code>[Result]<[TokenStream](AnyTokenStream), [Error]></code>.
836    #[allow(clippy::missing_errors_doc)]
837    fn convert(self) -> Result<TokenStream, Error>;
838}
839
840impl<T: AnyTokenStream> MacroOutput for T {
841    fn convert(self) -> Result<TokenStream, Error> {
842        Ok(self.into())
843    }
844}
845
846impl<T: MacroOutput, E: ToTokensError + 'static> MacroOutput for Result<T, E> {
847    fn convert(self) -> Result<TokenStream, Error> {
848        self.map_err(Error::from).and_then(MacroOutput::convert)
849    }
850}