Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

An attribute macro is a type of procedural macro in Rust that defines custom, compiler-recognized attributes. It operates as a function that takes a TokenStream (a flat sequence of lexical tokens) representing the annotated item, processes it at compile time, and emits a new TokenStream that entirely replaces the original item in the compilation unit. Unlike declarative macros (macro_rules!), attribute macros are compiled as separate dynamic libraries and are invoked by the compiler during the macro expansion phase, which is a distinct step that occurs after the initial parsing of the source code.

Crate Configuration

Attribute macros must be defined in a dedicated crate with the proc-macro type enabled in the Cargo.toml manifest:
[lib]
proc-macro = true

Function Signature

The macro is defined using the #[proc_macro_attribute] directive. The underlying Rust function must accept exactly two proc_macro::TokenStream arguments and return a single TokenStream.
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn custom_attribute(attr: TokenStream, item: TokenStream) -> TokenStream {
    // 1. Parse the attribute arguments (attr)
    // 2. Parse the annotated item (item)
    // 3. Transform the tokens
    // 4. Return the modified TokenStream
    item 
}

Parameter Breakdown

  1. attr: TokenStream: Represents the tokens passed inside the attribute’s parentheses. For #[custom_attribute(arg1, arg2)], this stream contains arg1, arg2. If no arguments are provided, this stream is empty.
  2. item: TokenStream: Represents the entire syntactic item (e.g., a struct, fn, enum, or impl block) to which the attribute is applied.

AST Manipulation

Because raw TokenStream manipulation is complex, attribute macros typically rely on the syn crate to explicitly parse the token stream into a strongly-typed Abstract Syntax Tree (AST), and the quote crate to serialize the modified AST back into a TokenStream.
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};

#[proc_macro_attribute]
pub fn modify_function(_attr: TokenStream, item: TokenStream) -> TokenStream {
    // Parse the input tokens into a syntax tree representing a function
    let input_fn = parse_macro_input!(item as ItemFn);
    
    let fn_block = &input_fn.block;
    let fn_vis = &input_fn.vis;
    let fn_sig = &input_fn.sig;

    // Reconstruct the function using quote!
    // Note: #fn_block is a syn::Block, which already includes the curly braces {}
    let expanded = quote! {
        #fn_vis #fn_sig #fn_block
    };

    // Convert the proc_macro2::TokenStream back to proc_macro::TokenStream
    TokenStream::from(expanded)
}

Application Syntax

Once defined and imported, the macro is applied to an item using the standard Rust attribute syntax. The compiler intercepts this, passes the item to the macro, and compiles the returned TokenStream instead of the original code.
use my_macro_crate::modify_function;

#[modify_function(optional_tokens)]
pub fn target_function() {
    println!("Original function body.");
}

Replacement Semantics

A critical characteristic of attribute macros is that they are destructive by default. The original item passed into the macro is discarded unless explicitly reconstructed and included in the returned TokenStream. If the macro returns an empty TokenStream, the annotated item is effectively stripped from the compiled source code.
Master Rust with Deep Grasping Methodology!Learn More