11个版本 (4个稳定版)
2.2.1 | 2024年6月14日 |
---|---|
2.1.0 | 2024年3月2日 |
2.0.0 | 2023年4月11日 |
1.0.0 | 2023年3月1日 |
0.0.3 | 2022年1月28日 |
#14 in 过程宏
每月230,306次下载
在531个库中使用(直接使用6个)
21KB
322 行
expander
将过程宏展开到文件中,并用include!
指令替换。
优点
- 仅展开特定的过程宏,而不是所有过程宏。例如,
tracing
因其使用cargo expand
展开成大量的样板代码而臭名昭著。 - 当你的生成的代码还不完美时,可以获得良好的错误信息
用法
在你的proc-macro
中,使用它如下
#[proc_macro_attribute]
pub fn baz(_attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// wrap as per usual for `proc-macro2::TokenStream`, here dropping `attr` for simplicity
baz2(input.into()).into()
}
// or any other macro type
fn baz2(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
let modified = quote::quote!{
#[derive(Debug, Clone, Copy)]
#input
};
let expanded = Expander::new("baz")
.add_comment("This is generated code!".to_owned())
.fmt(Edition::_2021)
.verbose(true)
// common way of gating this, by making it part of the default feature set
.dry(cfg!(feature="no-file-expansion"))
.write_to_out_dir(modified.clone()).unwrap_or_else(|e| {
eprintln!("Failed to write to file: {:?}", e);
modified
});
expanded
}
将展开成
include!("/absolute/path/to/your/project/target/debug/build/expander-49db7ae3a501e9f4/out/baz-874698265c6c4afd1044a1ced12437c901a26034120b464626128281016424db.rs");
文件内容将是
#[derive(Debug, Clone, Copy)]
struct X {
y: [u8:32],
}
示例输出
你的过程宏中的错误,例如多余的;
,显示为
Compiling expander v0.0.4-alpha.0 (/somewhere/expander) error: macro expansion ignores token `;` and any following --> tests/multiple.rs:1:1 | 1 | #[baz::baz] | ^^^^^^^^^^^ caused by the macro expansion here | = note: the usage of `baz::baz!` is likely invalid in item context error: macro expansion ignores token `;` and any following --> tests/multiple.rs:4:1 | 4 | #[baz::baz] | ^^^^^^^^^^^ caused by the macro expansion here | = note: the usage of `baz::baz!` is likely invalid in item context error: could not compile `expander` due to 2 previous errors warning: build failed, waiting for other jobs to finish... error: build failed
变为
Compiling expander v0.0.4-alpha.0 (/somewhere/expander) expander: writing /somewhere/expander/target/debug/build/expander-8cb9d7a52d4e83d1/out/baz-874698265c6c.rs error: expected item, found `;` --> /somewhere/expander/target/debug/build/expander-8cb9d7a52d4e83d1/out/baz-874698265c6c.rs:2:42 | 2 | #[derive(Debug, Clone, Copy)] struct A ; ; | ^ expander: writing /somewhere/expander/target/debug/build/expander-8cb9d7a52d4e83d1/out/baz-73b3d5b9bc46.rs error: expected item, found `;` --> /somewhere/expander/target/debug/build/expander-8cb9d7a52d4e83d1/out/baz-73b3d5b9bc46.rs:2:42 | 2 | #[derive(Debug, Clone, Copy)] struct B ; ; | ^ error: could not compile `expander` due to 2 previous errors warning: build failed, waiting for other jobs to finish... error: build failed
这显示在生成的代码中,你的过程宏的产物,rustc找到了无效的标记序列。
这是一个简单的例子,对于用cargo-expand
展开成成千上万行代码的宏来说,仍然是一个救命稻草,知道问题是由什么引起的,而不是需要使用eprintln!
将未格式化的字符串打印到终端。
提示:你可以通过使用
.dry(true || false)
快速切换此功能。
功能
特殊处理:syn
默认情况下,expander
与功能 syndicate
一起构建,这为 fn maybe_write_*
添加到 struct Expander
中,这有助于处理在常用 Rust 解析库 syn
中使用的 Result<TokenStream, syn::Error>
。
原因
syn::Error::new(Span::call_site(),"yikes!").into_token_stream(self)
会变成 compile_error!("yikes!")
,这比将其序列化到文件中提供了更好的用户信息(即您!),因为提供的 span
对 syn::Error
打印不同 - 在生成的文件中指向 compile_error! 调用没有帮助,而 rustc 可以指向 span。
rustfmt
无格式化:pretty
当与功能 pretty
一起构建时,输出会使用 prettier-please
进行格式化。请注意,这会在不使用任何宿主工具的情况下为 crate 增加额外的编译时开销和重量。
对于任何大量代码的行数,格式化输出将与 rustfmt
的输出不同。
依赖项
~0.5–1MB
~19K SLoC