1.0.0-rc4 2019年8月12日
1.0.0-rc2 2019年8月3日
1.0.0-rc1 2019年7月29日
0.15.0-rc4 2018年9月6日
0.15.0-rc3 2018年9月2日

#72 in #树节点

MIT/Apache

1MB
25K SLoC

Rust 源代码解析器

Build Status Latest Version Rust Documentation Rustc Version 1.31+

Syn 是一个解析库,可以将 Rust 标记流解析为 Rust 源代码的语法树。

目前,这个库主要针对 Rust 的过程宏使用,但包含一些可能更通用的 API。

  • 数据结构 — Syn 提供了一个完整的语法树,可以表示任何有效的 Rust 源代码。语法树以 syn::File 为根,它表示一个完整的源文件,但还有其他可能对过程宏有用的入口点,包括 syn::Itemsyn::Exprsyn::Type

  • 推导 — 对于推导宏来说,syn::DeriveInput 特别有用,它是推导宏的三个合法输入项之一。以下示例展示了在可以推导用户定义特质实现的库中使用此类型。

  • 解析 — Syn 中的解析基于签名为 fn(ParseStream) -> Result<T> 的解析函数。Syn 定义的每个语法树节点都可以单独解析,可以用作自定义语法的构建块,或者你可以梦想出全新的语法,而无需涉及我们的任何语法树类型。

  • 位置信息 — Syn解析的每个标记都与一个Span相关联,该Span跟踪行和列信息,回溯到该标记的来源。这些span允许过程宏显示指向用户代码中所有正确位置的详细错误信息。以下是一个示例。

  • 功能标志 — 功能性被积极封闭,所以您的过程宏只启用它们需要的功能,而不会在编译时间上为所有其他功能付费。

如果您在Rust中的过程宏方面遇到任何困难,即使问题与Syn无关,我也很乐意提供帮助。请在此存储库中提交工单。

版本要求:Syn支持rustc 1.31及更高版本。

发布说明


derive宏示例

使用Syn的规范derive宏看起来像这样。我们编写一个带有proc_macro_derive属性和我们要推导的特质名称的普通Rust函数。每当该derive出现在用户的代码中时,Rust编译器将他们的数据结构作为标记传递给我们的宏。我们有机会执行任意Rust代码来弄清楚如何处理这些标记,然后将一些标记返回给编译器,以编译到用户的crate中。

[dependencies]
syn = "0.15"
quote = "0.6"

[lib]
proc-macro = true
extern crate proc_macro;

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示例目录展示了derive宏的完整工作实现。它在任何Rust编译器1.31+上都能工作。该示例推导出HeapSize特质,该特质计算一个值拥有的堆内存的估计值。

pub trait HeapSize {
    /// Total number of bytes of heap memory owned by `self`.
    fn heap_size_of_children(&self) -> usize;
}

derive宏允许用户在程序中的数据结构上编写#[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,
}

通过跟踪span信息,正如heapsize示例中所示的过程宏的展开,基于标记的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 crate作为过程宏。

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 crate来编写测试错误,这些错误是由您的宏发出的或由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 — 用于遍历语法树的特质。
  • visit-mut — 用于就地遍历和修改语法树的特质。
  • fold — 用于转换自有语法树的特质。
  • clone-impls (默认启用)— 所有语法树类型的 Clone 实现。
  • extra-traits — 所有语法树类型的 Debug、Eq、PartialEq、Hash 实现。
  • proc-macro (默认启用)— 对 rustc 工具链中动态库 libproc_macro 的运行时依赖。

过程宏仿真层

Syn 使用 proc-macro2 crate 以稳定的方式在 Rust 1.15.0 及更高版本中模拟编译器的过程宏 API。这个仿真层使得编写代码时无需考虑当前编译器版本是否支持我们使用的功能。

通常,所有代码都应针对 proc-macro2 而非 proc-macro 编写。唯一的例外是过程宏入口点的签名,语言要求使用 proc_macro::TokenStream

在足够新的编译器上,proc-macro2 crate 会自动检测并使用编译器的数据结构。


许可证

根据您的选择,在 Apache License, Version 2.0 或 MIT 许可证下授权。请参阅 Apache 许可证,版本 2.0MIT 许可证
除非您明确声明,否则根据 Apache-2.0 许可证定义的,您有意提交以包含在此 crate 中的任何贡献,都应按照上述方式双授权,而不附加任何其他条款或条件。

依赖关系