How to debug macros?

So, I have the following macrocode that I am trying to debug. I took it from the Handbook in the "Deep End" section. I renamed the variables inside the macro to more closely monitor this post.

My goal is for the program to print each line of the BCT program. I know well that this is a very heavy compiler.

The only rustc error that gives me:

user@debian :~/rust/macros$ rustc --pretty expanded src/main.rs -Z unstable-options > src/main.precomp.rs src/main.rs:151:34: 151:35 error: no rules expected the token `0` src/main.rs:151 bct!(0, 1, 1, 1, 0, 0, 0; 1, 0); 

What steps can I take to find out where in the macro the problem arises from?

Here is my code:

 fn main() { { // "Bitwise Cyclic Tag" automation through macros macro_rules! bct { // cmd 0: 0 ... => ... (0, $($program:tt),* ; $_head:tt) => (bct_p!($($program),*, 0 ; )); (0, $($program:tt),* ; $_head:tt, $($tail:tt),*) => (bct_p!($($program),*, 0 ; $($tail),*)); // cmd 1x: 1 ... => 1 ... x (1, $x:tt, $($program:tt),* ; 1) => (bct_p!($($program),*, 1, $x ; 1, $x)); (1, $x:tt, $($program:tt),* ; 1, $($tail:tt),*) => (bct_p!($($program),*, 1, $x ; 1, $($tail),*, $x)); // cmd 1x: 0 ... => 0 ... (1, $x:tt, $($program:tt),* ; $($tail:tt),*) => (bct_p!($($program),*, 1, $x ; $($tail),*)); // halt on empty data string ( $($program:tt),* ; ) => (()); } macro_rules! print_bct { ($x:tt ; ) => (print!("{}", stringify!($x))); ( ; $d:tt) => (print!("{}", stringify!($d))); ($x:tt, $($program:tt),* ; ) => { print!("{}", stringify!($x)); print_bct!($program ;); }; ($x:tt, $($program:tt),* ; $($data:tt),*) => { print!("{}", stringify!($x)); print_bct!($program ; $data); }; ( ; $d:tt, $($data:tt),*) => { print!("{}", stringify!($d)); print_bct!( ; $data); }; } macro_rules! bct_p { ($($program:tt),* ; ) => { print_bct!($($program:tt),* ; ); println!(""); bct!($($program),* ; ); }; ($($program:tt),* ; $(data:tt),*) => { print_bct!($($program),* ; $($data),*); println!(""); bct!($($program),* ; $($data),*); }; } // the compiler is going to hate me... bct!(0, 1, 1, 1, 0, 0, 0; 1, 0); } 
+11
rust rust macros
source share
3 answers

There are two main ways to debug macros that don't expand:

  • trace_macros! and
  • log_syntax!

(NB, both are gated functions, in functions with the same name and therefore require the night compiler to work, multirust makes it easy to switch between versions for this kind of work.)

trace_macros!(...) accepts a boolean argument that enables or disables the trace macro (that is, it is stateful), if it is enabled, the compiler will print each macro call with its arguments as they expand. Usually you just need to call trace_macros!(true); at the top of the drawer, for example. if adds to the top of your code:

 #![feature(trace_macros)] trace_macros!(true); 

Then the output is as follows:

 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 the problem: calling bct_p! incorrect. bct_p carefully studied the problem, the left side of the second shoulder bct_p uses data:tt , when it should use $data:tt , i.e. Missing $ .

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

A commit that allows compilation to make progress.

log_syntax! not so useful in this case, but still a neat tool: it takes arbitrary arguments and prints them out when it expands, for example.

 #![feature(log_syntax)] log_syntax!("hello", 1 2 3); fn main() {} 

prints "hello" , 1 2 3 as it compiles. This is most useful for checking objects inside other macros.

(After you get the extension to work, the best tool for debugging 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 activate it.)

+18
source share

Another great tool that makes it easy to see expansion is load expansion.

Can be installed with:

 cargo install cargo-expand 

And then it is used quite simply as:

 cargo expand 

Or, with more accuracy, aim at a specific test file (for example, tests / simple.rs):

 cargo expand --test simple 

Be sure to check out --help , there are plenty of options to narrow down what is expanding. You can even target individual objects (structures, fns, etc.) to expand!

+1
source share

Debugging was interesting. I started with the simplest input and worked from there. I found that I had problems with the print functions (rewrite so that it just prints the inputs and doesn’t come back!).

I also added rules that were more explicit, and then deleted them as soon as everything worked (one by one, of course, testing along the way). As soon as I realized that every single part compiles and the print functions work, I was able to check the output of the macros. This macro sometimes runs when it shouldn't, but it compiles, prints, and debugs. I am happy enough with the current state to post it here.

 fn main() { // "Bitwise Cyclic Tag" automation through macros macro_rules! bct { // cmd 0: 0 ... => ... (0, $($program:tt),* ; $_head:tt) => (pbct!($($program),*, 0 ; )); (0, $($program:tt),* ; $_head:tt, $($tail:tt),*) => (pbct!($($program),*, 0 ; $($tail),*)); // cmd 1x: 1 ... => 1 ... x (1, $x:tt, $($program:tt),* ; 1) => (pbct!($($program),*, 1, $x ; 1, $x)); (1, $x:tt, $($program:tt),* ; 1, $($tail:tt),*) => (pbct!($($program),*, 1, $x ; 1, $($tail),*, $x)); // cmd 1x: 0 ... => 0 ... (1, $x:tt, $($program:tt),* ; $($tail:tt),*) => (pbct!($($program),*, 1, $x ; $($tail),*)); // halt on empty data string ( $($program:tt),* ; ) => (()); } macro_rules! println_bct { () => (println!("")); (;) => (println!(":")); ($d:tt) => (println!("{}", stringify!($d))); ($d:tt, $($data:tt),*) => { print!("{}", stringify!($d)); println_bct!($($data),*); }; ( ; $($data:tt),*) => { print!(":"); println_bct!($($data),*); }; ($x:tt ; $($data:tt),*) => { print!("{}", stringify!($x)); println_bct!( ; $($data),*); }; ($x:tt, $($program:tt),* ; $($data:tt),*) => { print!("{}", stringify!($x)); println_bct!($($program),* ; $($data),*); }; } macro_rules! pbct { ($($program:tt),* ; $($data:tt),*) => { println_bct!($($program),* ; $($data),*); bct!($($program),* ; $($data),*); }; } pbct!(0, 0, 1, 1, 1, 0, 0, 0 ; 1, 0, 1); // This one causes the compiler to hit recursion limits, heh // pbct!(0, 0, 1, 1, 1, 1, 1, 0 ; 1, 0, 1); } 
0
source share

All Articles