2 个不稳定版本
0.0.0+test | 2020 年 5 月 16 日 |
---|
#1501 in Rust 模式
每月 1,306 次下载
在 syn 中使用
2KB
Rust 源代码解析器
Syn 是一个解析库,可以将 Rust 令牌流解析为 Rust 源代码的语法树。
目前这个库主要面向 Rust 程序化宏的使用,但包含一些可能更通用的 API。
-
数据结构 — Syn 提供了一个完整的语法树,可以表示任何有效的 Rust 源代码。语法树以
syn::File
为根,它代表一个完整的源文件,但还有其他可能对程序化宏有用的入口点,包括syn::Item
、syn::Expr
和syn::Type
。 -
派生 — 对于派生宏来说,
syn::DeriveInput
特别有用,它是派生宏的三个合法输入项之一。以下示例展示了在一个可以派生用户定义特实现性的库中使用此类型。 -
解析 — Syn 中的解析基于具有签名
fn(ParseStream) -> Result<T>
的解析函数。Syn 定义的每个语法树节点都可以单独解析,并可以用作自定义语法的构建块,或者你可以梦想出你自己的全新的语法,而不涉及我们的任何语法树类型。 -
位置信息 — Syn 解析的每个标记都与一个
Span
相关联,该 Span 跟踪行和列信息,回到该标记的来源。这些 Span 允许过程宏显示指向用户代码中所有正确位置的详细错误消息。下面有一个例子。 -
功能标志 — 功能被积极地门控,因此您的过程宏只启用它们需要的功能,并且不需要在编译时间上为所有其他功能付费。
版本要求:Syn 支持 rustc 1.61 及以上版本。
资源
了解过程宏的最佳方式是编写一些。考虑通过 这个过程宏研讨会 来熟悉不同类型的过程宏。当您完成每个项目时,研讨会包含指向 Syn 文档的相关链接。
派生宏的示例
使用 Syn 的标准派生宏看起来是这样的。我们编写一个带有 proc_macro_derive
属性和我们要派生的特名字符的普通 Rust 函数。每次该派生出现在用户代码中时,Rust 编译器都会将他们的数据结构作为标记传递给我们的宏。我们可以执行任意的 Rust 代码来确定如何处理这些标记,然后将一些标记返回给编译器以编译到用户的包中。
[dependencies]
syn = "2.0"
quote = "1.0"
[lib]
proc-macro = true
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(MyMacro)]
pub fn my_macro(input: TokenStream) -> TokenStream {
// Parse the input tokens into a syntax tree
let input = parse_macro_input!(input as DeriveInput);
// Build the output, possibly using quasi-quotation
let expanded = quote! {
// ...
};
// Hand the output tokens back to the compiler
TokenStream::from(expanded)
}
heapsize
示例目录展示了派生宏的完整工作实现。该示例派生了一个 HeapSize
特性,该特性计算值拥有的堆内存的估计量。
pub trait HeapSize {
/// Total number of bytes of heap memory owned by `self`.
fn heap_size_of_children(&self) -> usize;
}
派生宏允许用户在他们的程序中的数据结构上编写 #[derive(HeapSize)]
。
#[derive(HeapSize)]
struct Demo<'a, T: ?Sized> {
a: Box<T>,
b: u8,
c: &'a str,
d: String,
}
Span 和错误报告
基于标记的过程宏 API 提供了对编译器错误消息在用户代码中显示位置的极大控制。考虑用户看到的错误,如果他们的字段类型没有实现 HeapSize
。
#[derive(HeapSize)]
struct Broken {
ok: String,
bad: std::thread::Thread,
}
通过跟踪 heapsize
示例中所示的派生宏的展开过程中的 Span 信息,基于标记的 Syn 宏能够触发直接指向问题源的错误。
error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied
--> src/main.rs:7:5
|
7 | bad: std::thread::Thread,
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HeapSize` is not implemented for `std::thread::Thread`
解析自定义语法
lazy-static
示例目录展示了使用 Syn 的解析 API 解析输入标记的 functionlike!(...)
派生宏的实现。
该示例重新实现了 crates.io 上的流行 lazy_static
包作为过程宏。
lazy_static! {
static ref USERNAME: Regex = Regex::new("^[a-z0-9_-]{3,16}$").unwrap();
}
该实现展示了如何在宏输入上触发自定义警告和错误消息。
warning: come on, pick a more creative name
--> src/main.rs:10:16
|
10 | static ref FOO: String = "lazy_static".to_owned();
| ^^^
测试
在测试宏时,我们通常不仅关心宏是否可以成功使用,而且当宏提供无效输入时,它会产生尽可能有帮助的错误消息。考虑使用 trybuild
包来编写测试宏产生的错误或 Rust 编译器在宏误用时检测到的错误。此类测试有助于避免在后续重构中意外地使错误不再触发或不如以前有帮助。
调试
在开发过程宏时,查看生成的代码可能会有所帮助。可以使用以下命令:cargo rustc -- -Zunstable-options --pretty=expanded
或 cargo expand
子命令。
要显示使用您过程宏的某些crate的展开代码,请在该crate中运行 cargo expand
。要显示您自己的测试用例之一的展开代码,请运行 cargo expand --test the_test_case
,其中最后一个参数是不带 .rs
扩展名的测试文件名。
Brandon W Maister 的这篇文章详细讨论了调试:[调试 Rust 的新自定义 derive 系统](https://quodlibetor.github.io/posts/debugging-rusts-new-custom-derive-system/)
可选功能
Syn 通过可选功能提供了许多功能,以优化常见用例的编译时间。以下功能可用。
derive
(默认启用)— 表示 derive 宏可能输入的数据结构,包括结构体、枚举和类型。full
— 表示所有有效 Rust 源代码的语法树的 数据结构,包括项和表达式。parsing
(默认启用)— 将输入标记解析为所选类型的语法树节点的能力。printing
(默认启用)— 将语法树节点打印为 Rust 源代码的标记。visit
— 遍历语法树的 trait。visit-mut
— 遍历和就地修改语法树的 trait。fold
— 用于转换自有语法树的 trait。clone-impls
(默认启用)— 所有语法树类型的 Clone 实现器。extra-traits
— 所有语法树类型的 Debug、Eq、PartialEq、Hash 实现器。proc-macro
(默认启用)— 对 rustc 工具链中动态库 libproc_macro 的运行时依赖。
过程宏包装器
Syn 在 proc-macro2 crate 提供的标记表示上操作,而不是直接使用编译器内置的过程宏 crate。这使得使用 Syn 的代码可以在过程宏的上下文之外执行,例如在单元测试或 build.rs 中,并且我们避免了过程宏和非宏用例不兼容的生态系统。
通常,所有代码都应该针对 proc-macro2 编写,而不是 proc-macro。唯一的例外是过程宏入口点的签名,语言要求使用 proc_macro::TokenStream
。
当过程宏活动时,proc-macro2 会自动检测并使用编译器的数据结构。
许可
根据您的选择,许可协议为 Apache License,Version 2.0 或 MIT 许可证:Apache 许可证,版本 2.0 或 MIT 许可证。除非您明确声明,否则您根据Apache-2.0许可证定义的任何有意提交以包含在此软件包中的贡献,将按照上述方式双授权,不附加任何其他条款或条件。