Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tips and tricks

Error management

While procedure macros do not return a Result it is still possible to report errors as part of the TokenStream. The easiest way to report an error is to use syn::Error. It takes a span as argument and an error message. The span identify where in the original source code the error message is originating. It is best to use a span coming from the most relevant token.

Example of usage:

#![allow(unused)]
fn main() {
let ts: TokenStream = syn::Error::new(
        ident.span(),
        "Something wrong happened."
    )
    .into_compile_error()
    .into()
}

Reference the crate

In declarative macros, to refer the current crate you can use $crate. There is no direct equivalent in proc_macros. The trick is to check the content of Cargo.toml using proc_macro_crate to check if the macro is invoked from the current crate or from an other.

You can use the following function:

#![allow(unused)]
fn main() {
/// This funcion is used to find if this macro is invoked from `my_macro_crate` and must be refered as `crate`
/// or if it is invoked from a different crate, and must be referred as `my_macro_crate`.
fn my_crate() -> proc_macro2::TokenStream {
    use proc_macro_crate::{FoundCrate, crate_name};
    let found_crate = crate_name("my_macro_crate")
        .expect("my_macro_crate is present in `Cargo.toml`");

    match found_crate {
        FoundCrate::Itself => quote!(crate),
        FoundCrate::Name(name) => {
            let ident = Ident::new(&name, proc_macro2::Span::call_site());
            quote!( #ident )
        }
    }
}
}

Identifier

quote does not allow to combine identifier inline. Templates fragments must be full token, if you need to create new identifier, they need to be created in code before been used in a quote!. The most basic solution is to create a new syn::Ident, for instance:

#![allow(unused)]
fn main() {
use proc_macro2::{Ident, Span};

let my_ident = Ident:new("my_ident", Span::call_site());
}

Span::call_site() will points any error related to my_ident, use it when creating identifier out of thin air. However, it is generally better to use a span from a token parsed from the macro invokation.

To create identifier from combining strings, quote provides a convenient formatting macro:

#![allow(unused)]
fn main() {
let my_ident = quote::format_ident!("my_{}", ident);
}

It will attempt to re-use the span from ident or create a new one.

For changing the case of identifier, between snake case (e.g. for functions or variables names) or pascal case (e.g. for struct), you can use the stringcase crate:

let function_name = Ident::new(stringcase::snake_case(ident.to_string()), ident.span());
let struct_name = Ident::new(stringcase::pascal_case(ident.to_string()), ident.span());