9

我正在寻找与file!()&module_path!()在程序宏上下文中的等价物。

例如,以下内容不起作用:

file.rs

#[some_attribute]
const A: bool = true;

macro.rs

#[proc_macro_attribute]
pub fn some_attribute(attr: TokenStream, input: TokenStream) -> TokenStream {
    println!("{}", file!());

    input
}

这打印macro.rs是有道理的,但我想要的是file.rs. 有没有办法做到这一点?是否也有类似的方法module_path!()

这样做的一个要求是必须在编译时发生。

我正在尝试在OUT_DIR包含常量值的文件中创建一个文件,其中属性与模块和它们所在的文件一起添加。

4

2 回答 2

5

我遇到了同样的问题,发现 Rust 为 Rust 宏(#54725)添加了一个新的实验性 API,它允许你想要什么:

#![feature(proc_macro_span)]

#[proc_macro]
pub(crate) fn do_something(item: TokenStream) -> TokenStream {
    let span = Span::call_site();
    let source = span.source_file();
    format!("println!(r#\"Path: {}\"#)", source.path().to_str().unwrap())
        .parse()
        .unwrap()
}
use my_macro_crate::*;

fn main() {
    println!("Hello, world!");
    do_something!();
}

将输出:

Hello, world!
Path: src\main.rs

重要的

除了这个 API 是实验性的之外,该路径可能不是真正的操作系统路径。如果Span是由宏生成的,则可能是这种情况。在此处访问文档。

于 2021-01-20T16:29:40.527 回答
1

这里的问题是println!("{}", file!());编译时而不是在运行时执行。与最近在此处给出的答案类似,您可以编辑原始函数并在其开头插入代码,这一次将在运行时执行。您仍然可以使用过程宏file!()module_path!(). 这是一种macro.rs采用这种方法的方法:

#[proc_macro_attribute]
pub fn some_attribute(_attr: TokenStream, input: TokenStream) -> TokenStream {

    // prefix to be added to the function's body
    let mut prefix: TokenStream = "
        println!(\"Called from {:?} inside module path {:?}\",
            file!(), module_path!());
    ".parse().unwrap();

    // edit TokenStream
    input.into_iter().map(|tt| { 
        match tt { 
            TokenTree::Group(ref g) // match function body
                if g.delimiter() == proc_macro::Delimiter::Brace => { 

                    // add logic before function body
                    prefix.extend(g.stream()); 

                    // return new function body as TokenTree
                    TokenTree::Group(proc_macro::Group::new(
                        proc_macro::Delimiter::Brace, prefix.clone()))
            },
            other => other, // else just forward
        }
    }).collect()
} 

你可以在你的main.rs

use mylib::some_attribute;

#[some_attribute]
fn yo() -> () { println!("yo"); }

fn main() { yo(); }

请注意,代码是在函数体内部的内容之前添加的。我们本可以在最后插入它,但这会破坏返回不带分号的值的可能性。

编辑:后来意识到 OP 希望它在compile time运行。

于 2020-03-18T13:08:40.083 回答