如何在Rust Proc Macro中获取保留空白符的原始源码?
获取Proc Macro输入的原始源码字符串
直接将TokenStream转为字符串会丢失空白符(换行、缩进等),因为TokenStream本质是抽象的token序列,不保留原始格式信息。要获取带原始格式的源码,得利用Rust提供的Span API来读取源码文本。
解决方案:利用Span获取原始源码
每个token都带有Span信息,标记了它在源码中的位置。我们可以合并输入所有token的span,得到覆盖整个宏输入范围的span,再通过source_text()方法读取对应位置的原始文本。
示例代码:
#[proc_macro] pub fn my_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let tokens: Vec<_> = input.into_iter().collect(); if tokens.is_empty() { return proc_macro::TokenStream::new(); } // 获取第一个和最后一个token的span,合并得到整个输入的范围 let full_span = tokens[0].span().join(tokens.last().unwrap().span()).unwrap(); // 读取原始源码文本 let source = full_span.source_text().unwrap(); println!("{}", source); // 输出会保留原始换行: // "{ // one // two // three // }" // 如果需要去掉外层大括号,可对字符串做处理 let inner_source = source.trim() .trim_start_matches('{') .trim_end_matches('}') .trim(); println!("{}", inner_source); // 输出:"one\ntwo\nthree" proc_macro::TokenStream::new() }
注意事项
- 版本要求:
Span::source_text()是Rust 1.59及以上的稳定API,确保你的编译环境满足版本要求。 - Span合并失败:
span.join()可能返回None(比如输入的token来自不同文件),实际使用中建议添加错误处理逻辑,避免panic。 - 复杂DSL处理:如果你的DSL结构复杂,可结合
syn库解析输入,通过解析后节点的span来获取源码,比手动处理token更可靠。
内容的提问来源于stack exchange,提问作者sak
相关产品推荐
相关产品推荐

