In this lesson, you will learn how to build procedural macros that scan the entire crate by capturing attributes placed at the crate root.
In Rust, attributes prefixed with #![...] are known as inner attributes. When placed at the very top of main.rs or lib.rs, they apply to the entire crate. While procedural macros (like #[derive] or function-like macros) usually target a specific item, an attribute macro applied to the crate root allows you to intercept the entire TokenStream of the crate.
The Mechanism
When you define an attribute macro and apply it to a module-level item, you are usually scoped to that specific item. However, when you use a custom attribute as #![my_macro::config(...)] at the top of your lib.rs, your procedural macro receives the entire crate's content as an input.
To process this, you need to use syn to parse the TokenStream into a File or ItemMod structure. Because the macro acts as the gatekeeper for the crate's contents, you can scan the AST to gather metadata, perform global checks, or inject boilerplate code into the root scope.
Illustrative Example
Consider a macro designed to enforce global API versioning. By applying it at the crate level:
#![my_meta::versioned(v1)]
fn main() { ... }
Inside your proc-macro, your entry point looks like this:
#[proc_macro_attribute]
pub fn versioned(args: TokenStream, input: TokenStream) -> TokenStream {
let file = parse_macro_input!(input as syn::File);
// You can now traverse the entire AST of the crate
// to check for compliance or inject global constants.
quote! {
#file
pub const CRATE_VERSION: &str = "v1";
}.into()
}
Challenges
The primary complexity here is that the macro consumes the crate content it is applied to. If you apply the macro to lib.rs, you effectively "wrap" the entire library. This is powerful for registry-style patterns (gathering all structs with a certain trait across the crate), but it requires careful management of the TokenStream to ensure you don't accidentally drop or reorder items in a way that breaks visibility rules or compilation.
Key Takeaway: Crate-level attributes allow procedural macros to intercept the entire root TokenStream, enabling global AST inspection and the automated injection of metadata across the entire project.