Macro proc_macro_utils::assert_expansion

source ·
macro_rules! assert_expansion {
    ($macro:ident!($($input:tt)*)$(.$fn:ident())?, { $($rhs:tt)* }) => { ... };
    ($macro:ident![$($input:tt)*]$(.$fn:ident())?, { $($rhs:tt)* }) => { ... };
    ($macro:ident!{$($input:tt)*}$(.$fn:ident())?, { $($rhs:tt)* }) => { ... };
    (#[derive($macro:ident)]$item:item$(.$fn:ident())?$(,)? { $($rhs:tt)* }) => { ... };
    (#[$macro:ident]$item:item$(.$fn:ident())?$(,)? { $($rhs:tt)* }) => { ... };
    (#[$macro:ident = $input:expr]$item:item$(.$fn:ident())?$(,)? { $($rhs:tt)* }) => { ... };
    (#[$macro:ident($($input:tt)*)]$item:item$(.$fn:ident())?$(,)? { $($rhs:tt)* }) => { ... };
    ($macro:ident($({$($input:tt)*}),+$(,)?)$(.$fn:ident())?, {$($rhs:tt)*}) => { ... };
}
Available on crate feature parser only.
Expand description

Allows simple unit testing of proc macro implementations.

This macro only works with functions taking proc_macro2::TokenStream due to the proc_macro API not being available in unit tests. This can be achieved either by manually creating a separate function:

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
#[proc_macro]
pub fn actual_macro(input: TokenStream) -> TokenStream {
    macro_impl(input.into()).into()
}
fn macro_impl(input: TokenStream2) -> TokenStream2 {
    // ...
}

or use a crate like manyhow:

use proc_macro2::TokenStream as TokenStream2;
#[manyhow(impl_fn)] // generates `fn actual_macro_impl`
pub fn actual_macro(input: TokenStream2) -> TokenStream2 {
    // ...
}

§Function like macros

// Dummy attribute macro impl
fn macro_impl(input: TokenStream) -> TokenStream {
    quote!(#input)
}
fn macro_impl_result(input: TokenStream) -> Result<TokenStream, ()> {
    Ok(quote!(#input))
}
assert_expansion!(
    macro_impl!(something test),
    { something test }
);
assert_expansion!(
    macro_impl![1, 2, 3],
    { 1, 2, 3 }
);
assert_expansion!(
    macro_impl!{ braced },
    { braced }
);
// adding a single function call (without arguments) is also allowed e.g. `unwrap()`
assert_expansion!(
    macro_impl_result!(result).unwrap(),
    { result }
);

§Derive macros

// Dummy derive macro impl
fn macro_impl(item: TokenStream) -> TokenStream {
    quote!(#item)
}
fn macro_impl_result(item: TokenStream) -> Result<TokenStream, ()> {
    Ok(quote!(#item))
}
assert_expansion!(
    #[derive(macro_impl)]
    struct A; // the comma after items is optional
    { struct A; }
);
assert_expansion!(
    #[derive(macro_impl)]
    struct A {}
    { struct A {} }
);
// adding a single function call (without arguments) is also allowed e.g. `unwrap()`
assert_expansion!(
    #[derive(macro_impl_result)]
    struct A {}.unwrap()
    { struct A {} }
);
// alternatively the proc_macro syntax is compatible
assert_expansion!(
    macro_impl!{ struct A {} },
    { struct A {} }
);

§Attribute macros

// Dummy attribute macro impl
fn macro_impl(input: TokenStream, item: TokenStream) -> TokenStream {
    quote!(#input, #item)
}
fn macro_impl_result(input: TokenStream, item: TokenStream) -> Result<TokenStream, ()> {
    Ok(quote!(#input, #item))
}
assert_expansion!(
    #[macro_impl]
    struct A;
    { , struct A; }
);
assert_expansion!(
    #[macro_impl = "hello"]
    fn test() { }, // the comma after items is optional
    { "hello", fn test() {} }
);
assert_expansion!(
    #[macro_impl(a = 10)]
    impl Hello for World {},
    { a = 10, impl Hello for World {} }
);
// adding a single function call (without arguments) is also allowed e.g. `unwrap()`
assert_expansion!(
    #[macro_impl_result(a = 10)]
    impl Hello for World {}.unwrap(),
    { a = 10, impl Hello for World {} }
);

§Generic usage

On top of the normal macro inputs a generic input is also supported.

fn macro_impl(first: TokenStream, second: TokenStream, third: TokenStream) -> TokenStream {
    quote!(#first, #second, #third)
}
fn macro_impl_result(first: TokenStream, second: TokenStream, third: TokenStream) -> Result<TokenStream, ()> {
    Ok(quote!(#first, #second, #third))
}
assert_expansion!(
    macro_impl({ 1 }, { something }, { ":)" }),
    { 1, something, ":)" }
);
// adding a single function call (without arguments) is also allowed e.g. `unwrap()`
assert_expansion!(
    macro_impl_result({ 1 }, { something }, { ":)" }).unwrap(),
    { 1, something, ":)" }
);