How do I debug macros?

Another great tool to use for easily seeing the expansion is cargo-expand.

It can be installed with:

cargo install cargo-expand

And then used quite simply as:

cargo expand

Or with more precision to target a particular test file (tests/simple.rs for example):

cargo expand --test simple

Be sure to check out the --help, there are a bunch of options to narrow down what is expanded. You can even target individual items (structs, fns etc.) for expansion!


There's two main ways to debug macros that are failing to expand:

  • trace_macros! and
  • log_syntax!

(NB. both are feature gated, under features of the same name, and so require the nightly compiler to work, multirust makes it easy to switch between versions for this sort of work.)

trace_macros!(...) takes a boolean argument that switches macro tracing on or off (i.e. it's stateful), if it's on, the compiler will print each macro invocation with its arguments as they are expanded. Usually one just wants to throw a trace_macros!(true); call at the top of the crate, e.g. if one adds the following to the top of your code:

#![feature(trace_macros)]

trace_macros!(true);

Then the output looks like:

bct! { 0 , 1 , 1 , 1 , 0 , 0 , 0 ; 1 , 0 }
bct_p! { 1 , 1 , 1 , 0 , 0 , 0 , 0 ; 0 }
<anon>:68:34: 68:35 error: no rules expected the token `0`
<anon>:68     bct!(0, 1, 1, 1, 0, 0, 0; 1, 0);
                                           ^
playpen: application terminated with error code 101

which hopefully narrows down the problem: the bct_p! call is invalid in some way. Looking at it carefully reveals the problem, the left-hand side of second arm of bct_p uses data:tt when it should use $data:tt, i.e. a missing $.

    ($($program:tt),* ; $(data:tt),*)

Fixing that allows compilation to make progress.

log_syntax! isn't as immediately useful in this case, but is still a neat tool: it takes arbitrary arguments and prints them out when it is expanded, e.g.

#![feature(log_syntax)]

log_syntax!("hello", 1 2 3);

fn main() {}

will print "hello" , 1 2 3 as it compiles. This is most useful to inspect things inside other macro invocations.

(Once you've got expansion to work, the best tool to debug any problems in the generated code is to use the --pretty expanded argument to rustc. NB. this requires the -Z unstable-options flag to be passed to activate it.)