Writing proc_macro that is only valid to be used on crate level like #![feature(...)]

Hi there,

I like to write a proc_macro within a proc_macro crate that does not 'decorate' any item and should only be valid on crate level (lib.rs or main.rs) like for example #![feature(...)].

Is this possible or are these kind of macros compiler magic?

Not all attributes are proc-macros; #![feature(...)] in particular is not.

AFAIK, it's not currently possible to usefully write a macro that operates on an entire crate (or even a module). What purpose would your macro serve? Maybe it can be implemented as a build.rs build script.

Hi,

well I‘m currently evaluating the use of a custom test framework which requires some specific boilerplate in every crate that uses it. Thus I was planning to build a kind of attribute macro which would generate the boilerplate code.

If I offload this into a build.rs file than the boilerplate still needs to be written for each crate but at a different place. I could also just use a simple macro_rules macro but I‘m trying to restrict its usage to either main.rs or lib.rs if possible. Otherwise I would just need to mention this in the macro docs.

I'd go with the documentation route other than relying on filenames.

  • If you're not using cargo, filenames have nothing to do with if you're a root library module, root binary module, or a sub-module, etc.
  • Even if you are using Cargo
    • binary roots can be named anything via Cargo.toml or by being in src/bin
    • non-root modules can be named lib or main

Etc, etc.

Well, my approach to use a proc_macro to generate the boiler plate does not to work as expected. My assumption was, that the macros get's expanded into source code as kind of a pre-compilation step. Thus the compiler would treat it as it has been written "by hand"... This seem not to be the case in my use case.

My macro shall create the following code e.g. when invoked in lib.rs.

#![cfg_attr(test, no_main)]
#![reexport_test_harness_main = "test_main"]
#![feature(custom_test_frameworks)]
#![test_runner(ruspiro_test_framework::test_runner)]
#[cfg(test)]
extern crate ruspiro_test_framework;
#[cfg(test)]
#[no_mangle]
extern "C" fn run_test_main() {
  test_main();
}

However, it creates this compiler error: an inner attribute is not permitted in this context. But if I "hand write" the same code the compiler is happy :thinking:

Does anyone has a guidance here? I really don't want that a user of the test-framework crate is required to manually write this boilerplate. This is error prone and would make it hard to roll out updates, fixes, enhancements to the framework as it may require to update the boilerplate code in any depending crate which could be solved by generating the code from a macro provided by the framework crate as well and thus both always play nicely together...

Crate-level proc-macros (and any application of a proc-macro to a whole module) are not yet supported in rustc. Until Tracking issue for custom inner attributes · Issue #54726 · rust-lang/rust · GitHub is finished, I think you must use the old way of transforming the code automatically: a build script (build.rs).