Course contentsShow
← Rust
Lesson 1540 of 1,91013. Metaprogramming with MacrosFree lesson

Span and Diagnostics API

You will generate precise compiler errors that point users toward invalid attribute placements.

In this lesson, you will learn how to provide professional-grade error reporting in your procedural macros by utilizing the Span and Diagnostics APIs to pinpoint exactly where user code goes wrong.

The Role of Spans

When a procedural macro consumes tokens, it isn't just dealing with code strings; it is dealing with TokenStream objects. Every Token inside that stream carries a Span—a hidden piece of metadata identifying its exact location (line, column, and file) in the source code.

If you generate code that violates a contract, simply returning a generic panic! makes it difficult for the user to debug. Instead, by attaching a Span to your errors, you tell the compiler exactly which part of the user's input triggered the failure.

Integrating Diagnostics

To create a high-quality developer experience, we use the proc_macro::Diagnostic API. Instead of just stopping the build, you can emit warnings or errors that visually highlight the problematic code:

use proc_macro::{Diagnostic, Level};

// Suppose we have a macro that expects an attribute on a struct
fn validate_input(input: TokenStream) -> TokenStream {
    if !is_struct(&input) {
        // Create a diagnostic pointing to the specific span
        Diagnostic::new(
            Level::Error,
            "This macro can only be applied to structs"
        )
        .span(input.span()) // Highlights the exact location
        .help("Try moving this to a struct definition instead.")
        .emit();
        
        return TokenStream::new();
    }
    // ... continue generation
}

By calling .emit(), you instruct the compiler to render a professional error message. The Span ensures the red squiggly line in the user's IDE appears exactly where it belongs, rather than at the macro invocation site. This API allows you to chain .help() or .note() calls, effectively bringing your custom macro's error handling up to the standard of the official Rust compiler itself.

Key Takeaway: Use the Span attached to your input tokens to anchor your Diagnostics, ensuring that your macro errors are as clear, localized, and actionable as the compiler's own native messages.