Debugging

rustc provides a number of tools to debug macro_rules! macros. One of the most useful is trace_macros!, which is a directive to the compiler instructing it to dump every macro_rules! macro invocation prior to expansion. For example, given the following:

// Note: make sure to use a nightly channel compiler.
#![feature(trace_macros)]

macro_rules! each_tt {
    () => {};
    ($_tt:tt $($rest:tt)*) => {each_tt!($($rest)*);};
}

each_tt!(foo bar baz quux);
trace_macros!(true);
each_tt!(spim wak plee whum);
trace_macros!(false);
each_tt!(trom qlip winp xod);

fn main() {}

The output is:

note: trace_macro
  --> src/main.rs:11:1
   |
11 | each_tt!(spim wak plee whum);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: expanding `each_tt! { spim wak plee whum }`
   = note: to `each_tt ! (wak plee whum) ;`
   = note: expanding `each_tt! { wak plee whum }`
   = note: to `each_tt ! (plee whum) ;`
   = note: expanding `each_tt! { plee whum }`
   = note: to `each_tt ! (whum) ;`
   = note: expanding `each_tt! { whum }`
   = note: to `each_tt ! () ;`
   = note: expanding `each_tt! {  }`
   = note: to ``

This is particularly invaluable when debugging deeply recursive macro_rules! macros. You can also enable this from the command-line by adding -Z trace-macros to the compiler command line.

Secondly, there is log_syntax! which causes the compiler to output all tokens passed to it. For example, this makes the compiler sing a song:

// Note: make sure to use a nightly channel compiler.
#![feature(log_syntax)]

macro_rules! sing {
    () => {};
    ($tt:tt $($rest:tt)*) => {log_syntax!($tt); sing!($($rest)*);};
}

sing! {
    ^ < @ < . @ *
    '\x08' '{' '"' _ # ' '
    - @ '$' && / _ %
    ! ( '\t' @ | = >
    ; '\x08' '\'' + '$' ? '\x7f'
    , # '"' ~ | ) '\x07'
}

fn main() {}

This can be used to do slightly more targeted debugging than trace_macros!.

Sometimes, it is what the macro expands to that proves problematic. For this, the --pretty argument to the compiler can be used. Given the following code:

// Shorthand for initialising a `String`.
macro_rules! S {
    ($e:expr) => {String::from($e)};
}

fn main() {
    let world = S!("World");
    println!("Hello, {}!", world);
}

compiled with the following command:

rustc -Z unstable-options --pretty expanded hello.rs

produces the following output (modified for formatting):

#![feature(no_std, prelude_import)]
#![no_std]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std as std;
// Shorthand for initialising a `String`.
fn main() {
    let world = String::from("World");
    ::std::io::_print(::std::fmt::Arguments::new_v1(
        {
            static __STATIC_FMTSTR: &'static [&'static str]
                = &["Hello, ", "!\n"];
            __STATIC_FMTSTR
        },
        &match (&world,) {
             (__arg0,) => [
                ::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Display::fmt)
            ],
        }
    ));
}

Other options to --pretty can be listed using rustc -Z unstable-options --help -v; a full list is not provided since, as implied by the name, any such list would be subject to change at any time.

But not just rustc exposes means to aid in debugging macros. For the aforementioned --pretty=expanded option, there exists a nice cargo addon called cargo-expand made by dtolnay which is basically just a wrapper around it.

You can also use the playground, clicking on its TOOLS button in the top right gives you the option to expand macros right there!

Another amazing tool is lukaslueg's macro_railroad, a tool that allows you visualize and generate syntax diagrams for Rust's macro_rules! macros. It visualizes the accepted macro's grammar as an automata.