关于 macros-code-review
面向 Rust 开发者的宏代码审查技能,覆盖声明式宏和过程宏。工作流强调检查 crate 上下文、阅读完整定义、为问题提供证据并通过验证关卡。
主要功能
- 01按宏类型进行审查的流程
- 02Cargo.toml 与 edition 检查
- 03声明式宏检查清单
- 04过程宏检查清单
- 05证据与验证关卡
适用场景
- 01合并前审查宏代码
- 02检查生成代码的 edition 问题
- 03评估宏错误处理与卫生性
- 04审计宏依赖和功能标志
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ existential-birds/ beagle --skill macros-code-review安装后在 Claude Code 中输入/macros-code-review确认已加载。
附带文件references/declarative-macros.mdreferences/procedural-macros.md
详细介绍
宏代码审查
审查工作流
- 检查
Cargo.toml-- 留意 Rust 版本(2024 版保留了gen关键字,会影响宏输出)、过程宏 crate 依赖(syn、quote、proc-macro2)和功能标志(例如,为syn启用最少的功能) - 检查宏类型 -- 确定要审查的是声明式宏(
macro_rules!)、类函数过程宏、属性宏还是派生宏 - 检查是否需要宏 -- 如果转换基于类型,泛型会更合适。宏用于生成泛型无法表达的结构性/重复代码
- 浏览宏定义 -- 阅读完整的宏主体,包括所有匹配分支,而不只是调用位置
- 逐项检查 -- 按照下方的清单进行检查,并根据需要加载参考资料
- 关卡 -- 报告前完成下方的关卡;不要用非正式的“我已验证”代替
关卡(报告发现前)
按顺序完成。每个问题都必须通过关卡 4 后,才能提交发现。
关卡 1 — Crate 上下文(磁盘上的文件)
通过条件: 已打开被审查 crate 的 Cargo.toml(如适用,则为工作区成员路径下的文件),并记录 Rust edition、该 crate 是否设置了 proc-macro = true,以及相关的过程宏依赖或 syn/quote 功能标志。
防止误判: 针对 2024 版的发现(gen、unsafe extern、生成的 unsafe 主体)以及 syn 的“full”与最少功能标志相关发现都需要此信息——未根据文件确认 edition 前,不要指出特定版本下的宏输出问题。
关卡 2 — 阅读宏定义
通过条件: 对于每个要评审的宏,都已阅读完整定义(所有 macro_rules! 分支,或过程宏入口及所依赖的辅助函数),而不只是调用位置或部分展开结果。
记录: 至少为每个宏提供一个所参考的定义文件 .rs 路径。
关卡 3 — 每项发现的证据
通过条件: 每个计划提出的问题都有当前代码树中的 [FILE:LINE],对应你讨论的宏定义、属性/派生位置或生成代码位置(不能凭记忆、仅依据文档或引用其他分支)。
关卡 4 — 报告前流程
通过条件: 已加载并应用 review-verification-protocol 技能,包括针对卫生性、片段类型和过程宏性能主张的宏专项验证。之后再添加发现。
输出格式
按以下格式报告发现:
[FILE:LINE] ISSUE_TITLE
Severity: Critical | Major | Minor | Informational
Description of the issue and why it matters.
快速参考
| 问题类型 | 参考资料 |
|---|---|
片段类型、重复、卫生性边界(变量与类型)、$crate 路径、TT-muncher 模式与递归限制、片段匹配器后续限制、声明式宏与过程宏决策树 | references/declarative-macros.md |
过程宏类型、syn/quote、跨度卫生性(call_site 与 def_site 与 mixed_site)、syn::Error::new_spanned 与 combine、parse_quote! 与 quote!、syn 功能审查、trybuild UI 测试 | references/procedural-macros.md |
审查清单
声明式宏(macro_rules!)
- 使用正确的片段类型(
:expr与:tt、:ident的选择错误会导致意外解析) - 重复分隔符符合预期语法(
,、;或无分隔符;*或+) - 处理末尾的逗号/分号(在重复末尾添加
$(,)?或$(;)?) - 匹配器按从最具体到最不具体的顺序排列(优先匹配第一个)
- 展开不存在歧义——每个元变量在转写器中都位于正确的重复层级
- 宏中定义的变量使用宏内部名称(卫生性可保护变量,但不保护类型/模块/函数)
- 导出的宏(
#[macro_export])使用$crate::引用 crate 内部路径,绝不使用crate::或self:: - 标准库路径使用
::core::和::alloc::(而非::std::),以兼容no_std - 对无效输入模式使用
compile_error!,提供有意义的错误消息 - 宏的位置遵循文本作用域规则(使用前定义),除非使用
#[macro_export]
过程宏
- 尽量精简
syn特性(够用derive时不要启用full,以缩短编译时间) - 将输入 token 的跨度传播到输出 token(错误应指向用户代码,而非宏内部)
- 引入辅助变量时默认使用
Span::mixed_site();仅在有意指向用户代码时使用call_site;proc_macro::Span上的def_site仅限 nightly - 使用
syn::Error::new_spanned(node, msg)和出错的 AST 节点报告错误,绝不使用panic! - 通过
syn::Error::combine收集并一并报告多个错误(改善用户体验,避免只报告首个错误) - 当结果需要作为
syn::T以便进一步操作 AST 时,使用parse_quote!(而非quote!);可能失败时使用syn::parse2(quote!{...}) - 从
proc_macro2重新导出公共类型,而非从proc_macro重新导出(确保与下游使用者兼容) - 使用
proc-macro2进行测试(可在编译器上下文之外测试) - 生成的代码量应适度——过程宏生成大量代码会拖慢编译
派生宏
- 派生行为显而易见——开发者仅凭 trait 名称就能猜出其功能
- 记录辅助属性(如
#[serde(skip)]风格的属性) - 对所有变体形式(单元变体、元组变体、结构体变体)都能正确实现 trait
- 生成的
impl块使用完全限定路径(::core::、$crate::)
属性宏
- 保留输入项,或按预期进行转换(不得意外丢弃)
- 使用清晰的错误消息验证属性参数
- 测试生成模式(如
#[test_case]风格)应生成唯一的测试名称 - 框架注解应说明其生成的代码
Edition 2024 注意事项
- 宏输出不得将
gen用作标识符(它是保留关键字——请使用r#gen或重命名) - 生成的
unsafe fn函数体应在不安全操作周围使用显式的unsafe {}块 - 生成的
extern块应使用unsafe extern
泛型与宏
如果使用泛型或 trait bound 可以实现相同结果,则应标记该宏。以下情况适合使用宏:
- 生成的代码在结构上有所变化(而不只是类型不同)
- 为许多具体类型重复实现 trait
- 带有配置变体的测试集
const fn无法表达的编译期计算
严重性评定
严重(阻止合并)
- 宏生成了不健全的
unsafe代码 - 输出
unsafe块的宏存在卫生性违规(调用方的变量泄漏到不安全上下文中) - 过程宏发生 panic,而不是返回
compile_error!(导致编译器崩溃) - 派生宏生成了错误的 trait 实现(违反 trait 契约)
重大(应修复)
- 导出的宏使用
crate::或self::,而不是$crate::(会导致下游用户无法使用) - 导出的宏使用
::std::,而不是::core::/::alloc::(会导致no_std用户无法使用) - 片段类型错误,导致解析结果异常(需要
:tt时用了:expr,或反之) - 过程宏不必要地启用了
syn的 full 功能(增加编译时间) - 未传播 span(错误指向宏定义,而非调用处)
- 过程宏没有错误处理(遇到错误输入时会 panic,而不是使用
compile_error!)
次要(考虑修复)
- 重复模式未容忍末尾的逗号/分号
- 匹配分支未按特定性从高到低排列
- 使用泛型会更清晰且表达能力相当,却选择了宏
- 缺少用于无效模式的
compile_error!回退分支 - 未记录辅助属性
仅供参考(注意即可)
- 建议将复杂的
macro_rules!拆分为过程宏 - 建议减少生成的代码量
- 可以简化 TT munching 或 push-down accumulation 模式
有效模式(不要标记)
- 用于测试套件的
macro_rules!——根据类型/配置列表生成重复的测试模块 - 用于 trait 实现的
macro_rules!——为多个具体类型实现具有相同主体的 trait - TT munching——用于递归处理 token 的有效高级模式
- Push-down accumulation——用于在递归调用中逐步构建输出的有效模式
- 使用
$crate的#[macro_export]——这是让宏可在定义它的 crate 之外使用的正确方式 - 为生成的函数使用
Span::call_site()——有意让生成的条目对调用者可见 syn::Error::to_compile_error()——过程宏中正确的错误报告模式- 过程宏的
trybuild测试——标准的编译失败测试方法 - 测试函数上的属性宏——常见的测试设置/清理模式
- 在不可能匹配的分支中使用
compile_error!——捕获无效宏输入的良好做法