0

Is there a way in Rust to get the "calling" function name or any other contextual information inside a macro?

Example:

#[macro_export]
macro_rules! somemacro {
    ( $x:expr ) => {
        {
          // access function name (etc..) that called this macro
        }
    };
}
Techradar
  • 3,506
  • 3
  • 18
  • 28
  • 1
    Is adding an attribute to the function an option? (eg. `#[with_name] fn foo() {}`) – mcarton Jun 22 '19 at 19:42
  • 1
    Maybe this helps: https://stackoverflow.com/questions/38088067/equivalent-of-func-or-function-in-rust – phimuemue Jun 22 '19 at 20:00
  • @mcarton It is I guess... but I don't understand how to access that in an example, where the macro would be called inside a function. Could you show an example? What I understand from the link provided by phimuemue, the macro needs to be tagged according to different attributes. – Techradar Jun 23 '19 at 06:34

1 Answers1

7

This can be done using a procedural macro:

extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn with_name(_: TokenStream, item: TokenStream) -> TokenStream {
    let mut input = syn::parse_macro_input!(item as syn::ItemFn);

    let fn_name = input.ident.to_string();
    let const_decl = quote::quote! {
        const THIS_FN: &str = #fn_name;
    };

    input.block.stmts.insert(0, syn::parse(const_decl.into()).unwrap());

    let output = quote::quote! {
        #input
    };

    output.into()
}

Cargo.toml:

[package]
name = "with_name"
version = "0.1.0"
edition = "2018"

[lib]
proc-macro = true

[dependencies]
quote = "0.6.12"
syn = { version = "0.15.37", features = ["full"] }

Which can be used as:

#[with_name::with_name]
fn foo() {
    println!("Name: {}", THIS_FN);
}

fn main() {
    foo();
}

Also note that if you only care about the module, there is a built-in macro for that:

mod test {
    pub fn foo() {
        println!("Module: {}", module_path!());
    }
}

fn main() {
    test::foo();
}

(link to playground)

mcarton
  • 27,633
  • 5
  • 85
  • 95