有没有办法在编译时获取程序宏所在的文件和模块路径?
Is there a way to get the file and the module path of where a procedural macro is attached at compile-time?
我正在寻找程序宏上下文中 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
中创建一个包含常量值的文件,其中属性与模块和它们所在的文件一起添加。
这里的问题是 println!("{}", file!());
在 编译时 而不是在 运行 时执行。类似于最近给出的答案,您可以编辑原始函数并在其开头插入您的代码,该代码将在运行时间[=30=执行] 这次。您仍然可以使用程序宏 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希望它在编译时运行。
我遇到了同样的问题,发现 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 是实验性的,该路径可能不是真正的 OS 路径。如果 Span
是由宏生成的,就会出现这种情况。访问文档 here.
我正在寻找程序宏上下文中 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
中创建一个包含常量值的文件,其中属性与模块和它们所在的文件一起添加。
这里的问题是 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希望它在编译时运行。
我遇到了同样的问题,发现 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 是实验性的,该路径可能不是真正的 OS 路径。如果 Span
是由宏生成的,就会出现这种情况。访问文档 here.