macro_rules! bench_modules {(
    $(
        $( #[$attr:meta] )*
        $pub:vis
        mod $module:ident ;
    )*
) => (
    $(
        $( #[$attr] )*
        $pub
        mod $module;
    )*

    ::criterion::criterion_main! {
        $(
            $module::__group__ ,
        )*
    }
)}

macro_rules! auto_bench {
    // End of recursion
    (
        @acc[ $($names:ident)* ]
        /* Nothing left to parse */
    ) => (
        // Time to use the found function names: call `criterion_group!`:
        criterion_group! {
            __group__, $($names),*
        }
    );

    // Recursion step: parse a `#[bench]`-annotated fn
    (
        @acc[ $($names:ident)* ]
        #[bench]
        $(#[$attr:meta])*
        $pub:vis
        fn $fname:ident ($fname_stringified:ident : & $('_)? str, $($args:tt)*)
        {
            $($body:tt)*
        }

        $($rest:tt)*
    ) => (
        // emit with function name added
        $(#[$attr])*
        pub(in crate)
        fn $fname ($($args)*)
        {
            let $fname_stringified =
                crate::helpers::type_name_of_val(&$fname)
                    .splitn(2, "::")
                    .nth(1)
                    .unwrap()
            ;
            $($body)*
        }
        // recurse / step
        auto_bench! {
            @acc[ $($names)* $fname ]
            $($rest)*
        }
    );

    // Recursion step: parse (and ignore) some other item
    (
        @acc $acc:tt
        $item:item
        $($rest:tt)*
    ) => (
        // emit as-is
        $item
        // recurse / step
        auto_bench! {
            @acc $acc
            $($rest)*
        }
    );

    // Entry point
    (
        $($input:tt)*
    ) => (
        #[allow(unused_imports)]
        use ::criterion::criterion_group; // dummy criterion requires this to be in scope.

        auto_bench! {
            @acc[]
            $($input)*
        }
    );
}
