Using proc-macro for local and user crates

My crate had an internal proc-macro, which used crate:: to access all values defined inside.
Now I need to extend this crate and allow users to use it as well. Since there is no $crate meta-variable for proc-macro, I defined this...

use std::cell::Cell;

thread_local! {
    pub static MODULE: Cell<&'static syn::Ident> = {
        let crate_name = std::env::var("CARGO_PKG_NAME").unwrap();
        let module = match crate_name.as_str() {
            "my_crate" => "crate",
            _ => "my_crate",
        };
        Cell::new(Box::leak(Box::new(ident(module))))
    }
}

... with the idea of using it like so:

let module = MODULE.get();
syn::parse_quote! { #module::fun()  }

Sadly, the second I try to access this identifier in syn::parse_quote! the compiler crashes:

$ cargo check
error: could not compile `my_crate` (lib)
Caused by:
  process didn't exit successfully: ...  (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN)

The solution I came up with is hacky at best, so what would be the proper way of doing this?
Also do I understand correctly that this is a bug in the compiler? It happens both on nightly and stable-1.86.

this is a known limitation of procedural macros today, and all the solutions in-use are all "hacky", unfortunately. for example, snafu uses a custom attribute.

see also discussions in this issue:

2 Likes

There is a crate called find-crate that I have tried, and there may be other options, but I would rather agree with @nerditation and recommend a custom attribute. Most projects don't need it in their dependency tree and the few who will end up renaming your crate may even appreciate the explicit control with the attribute. serde did the same thing, as another example. You can let it default to the crate name and override it internally.

1 Like

Do not try storing any state across proc-macro invocations. There is no guarantee that all proc-macro invocations are for the same process. For example rust-analyzer will load proc-macros once and reuse them for all crates in the workspace.

Almost all types in the proc_macro crate are actually handles pointing to a table of rustc types. This table is cleared between proc-macro invocations, so your attempt at caching a proc-macro value will cause a dangling handle. Dereferencing this dangling handle will cause a panic. And then for some reason this panic didn't get turned into a user visible error, but turned into an abort() call, which confusingly causes a STATUS_STACK_BUFFER_OVERRUN exit code: STATUS_STACK_BUFFER_OVERRUN doesn't mean that there was a stack buffer overrun - The Old New Thing

1 Like