2

I'm trying to debug a complex procedural macro in a library I am using.

Since I cannot use a debugger with macros and various macro expansion tools have proved useless here, I'm looking for an alternative.

Can a procedural macro be run like a function with proper debugging? I imagine storing the resultant proc_macro::TokenStream in a variable.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Jonathan Woollett-light
  • 2,813
  • 5
  • 30
  • 58

1 Answers1

8

The proc-macro2 crate is a drop-in replacement for proc_macro except that it is available outside of macros - which makes it testable. Its types are all convertible to and from the proc_macro types and have identical methods.

The usual pattern for writing a nontrivial macro is to use proc_macro only for the entry point, and use proc-macro2 for all of the real work:

extern crate proc_macro;
use proc_macro2::TokenStream;

#[proc_macro]
pub fn my_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let output = transform_stream(TokenStream::from(input));
    proc_macro::TokenStream::from(output)
}

// A testable function!
fn transform_stream(input: TokenStream) -> TokenStream {
    // actual work goes here
}

It's common to import items from proc-macro2 so they can be used unqualified, and just used fully qualified names for proc_macro, since the only time you will use it is in the entry point. It's also usual to put the core components in a separate library crate, which does not have a dependency on proc_macro.


In tests, you can create a TokenStream from a string:

use std::str::FromStr;
let ts = TokenStream::from_str("fn foo() {}").unwrap();
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Would something like https://pastebin.com/WpWCmtAk work? How would I initiate a `TokenStream` to call `transform_stream` with? (The actual macro I'm working on: https://docs.rs/vulkano-shaders/0.19.0/src/vulkano_shaders/lib.rs.html#320-365) – Jonathan Woollett-light Aug 07 '20 at 20:09
  • Hastebin alternative of the pastebin: https://hastebin.com/cawihocuxu.php (pastebin was being a bit weird) – Jonathan Woollett-light Aug 07 '20 at 20:30
  • @JonathanWoollett-light you can just create TokenStream from a string: `TokenStream::from_str("fn foo() { }").unwrap();` – Peter Hall Aug 13 '20 at 11:31