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}