Modifying macro results inside procedural macro

Imagine I have this:

mod foo {
    use procedural::UniqueName;
    pub struct Bar {}

and we are going to generate a unique name for any struct by applying UniqueName procedural macro. So the implementation for procedural macro should be something like this:

  pub fn derive_unique_name(input: TokenStream) -> TokenStream {
    let ast: syn::DeriveInput = syn::parse(input.clone()).unwrap();
    let ident = &ast.ident;

  let module_path = get_module_path(...); // Evil is here!
  let unique_name = module_path.replace("::", "_");

  let gen = quote! {
        impl #ident {
            pub const name: &'static str = #unique_name;

Things so far is easy. But implementing get_module_path(...) is not easy (as I tried). We can get module_path by calling module_path!() macro like:

let module_path:TokenStream = TokenStream::from(quote! { module_path!();});

But it's TokenStream. Another way is parsing TokenStream input which it is hard and I couldn't find information about the parent folder.

Do you have any idea?

FYI, TokenStream::from(quote! { module_path!();}); won't return to you the module path of the item. quote! literally generates a token stream corresponding to its argument, so it will return the token stream [identifier "module_path", punctuation "!", parentheses "()"].

I don't think procedural macros can inspect the environment of their inputs like this, only the immediate input, i.e. the literal contents of the type declaration, is available to them, because they run well before typechecking.

What you can do is generate code that parses the return value of module_path!, but it won't then be a string literal. You can wrap it in, say, a Lazy, so you would generate something like:

 let gen = quote! {
    impl #ident {
        pub const NAME: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(
            || module_path!().replace("::", "_")

It isn't, really. It's designed to be parsed (and that's pretty much the only useful, non-trivial operation you can do with it). If you have a ts: TokenStream, then you can use syn::parse::<Path>(ts) in order to get a syn::Path.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.