#proc-macro #macro-expansion #debugging #expand #output #generated #error

expander

将过程宏的输出展开到文件中,以便更容易进行调试

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 过程宏

Download history 59191/week @ 2024-05-02 49541/week @ 2024-05-09 42390/week @ 2024-05-16 55688/week @ 2024-05-23 50916/week @ 2024-05-30 38182/week @ 2024-06-06 41773/week @ 2024-06-13 45487/week @ 2024-06-20 42847/week @ 2024-06-27 42616/week @ 2024-07-04 51662/week @ 2024-07-11 57838/week @ 2024-07-18 53937/week @ 2024-07-25 48378/week @ 2024-08-01 62368/week @ 2024-08-08 54441/week @ 2024-08-15

每月230,306次下载
531库中使用(直接使用6个)

MIT/Apache

21KB
322

crates.io CI commits-since rust 1.65.0+ badge

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!"),这比将其序列化到文件中提供了更好的用户信息(即您!),因为提供的 spansyn::Error 打印不同 - 在生成的文件中指向 compile_error! 调用没有帮助,而 rustc 可以指向 span

rustfmt 无格式化:pretty

当与功能 pretty 一起构建时,输出会使用 prettier-please 进行格式化。请注意,这会在不使用任何宿主工具的情况下为 crate 增加额外的编译时开销和重量。

对于任何大量代码的行数,格式化输出将与 rustfmt 的输出不同。

依赖项

~0.5–1MB
~19K SLoC