无需分叉Clippy即可编写Rust代码检查工具
Rust代码检查与Clippy
像Clippy这样的工具利用了Rust编译器对代码检查的专用支持。Rust检查器的核心组件称为"驱动程序",它链接到一个名为rustc_driver
的库。通过这种方式,驱动程序本质上成为Rust编译器的包装器。
要运行检查器,需要将RUSTC_WORKSPACE_WRAPPER
环境变量设置为指向驱动程序,然后运行cargo check
。Cargo注意到环境变量已设置,会调用驱动程序而不是直接调用rustc
。当驱动程序被调用时,它会在Rust编译器的Config结构中设置回调。该回调注册一定数量的检查规则,然后Rust编译器会与其内置检查规则一起运行这些规则。
Clippy执行一些检查以确保其已启用,但其他方面以上述方式工作。虽然安装时可能不太明显,但Clippy实际上是两个二进制文件:一个Cargo命令和一个rustc驱动程序。您可以通过以下命令验证:
|
|
Dylint的工作原理
与Clippy类似,Dylint提供了一个Cargo命令。用户向该命令指定要从哪些动态库加载检查规则。Dylint以确保在控制权交给Rust编译器之前注册检查规则的方式运行cargo check
。
然而,Dylint的检查规则注册过程比Clippy更复杂。Clippy的所有检查规则都使用相同的编译器版本,因此只需要一个驱动程序。但Dylint用户可以选择从使用不同编译器版本的库加载检查规则。
Dylint通过根据需要即时构建新的驱动程序来处理这种情况。换句话说,如果用户想要从使用编译器版本A的库加载检查规则,但找不到适用于编译器版本A的驱动程序,Dylint将构建一个新的驱动程序。驱动程序缓存在用户的主目录中,因此只在必要时重新构建。
这带来了上一节提到的额外好处。Dylint按使用的编译器版本对库进行分组。使用相同编译器版本的库被一起加载,它们的检查规则一起运行。这允许在检查规则之间共享中间编译结果(例如,符号解析、类型检查、特征求解等)。
应用:项目特定检查规则
您知道吗?Clippy包含一些专门用于检查Clippy自身代码的检查规则。这是真的。例如,Clippy包含检查每个检查规则是否有相关的LintPass、是否使用某些Clippy包装函数而不是它们包装的函数,以及每个检查规则是否有非默认描述的检查规则。
Dylint同样包含主要目的是检查Dylint代码的检查规则。例如,在开发Dylint时,我们发现自己编写了如下代码:
|
|
这是不好的做法。更好的方法是使用常量而不是字符串字面量:
|
|
因此,在开发Dylint时,我们编写了一个检查规则来检查这种不良做法并提出适当建议。该检查规则名为env_literal
,其当前实现的核心部分如下:
|
|
这是一个可能产生的警告示例:
|
|
开始使用检查工具
使用以下命令安装Dylint:
|
|
我们还建议安装dylint-link
工具以方便链接:
|
|
编写Dylint库的最简单方法是分叉dylint-template
存储库。该存储库开箱即用地生成可加载库。您可以通过以下方式验证:
|
|
您只需要实现LateLintPass
特性并填充需要填充的符号。
编写检查规则的有用资源包括:
- 添加新检查规则(针对Clippy但仍然有用)
- 编写检查规则的常用工具
- rustc_hir文档
还可以考虑使用上面提到的clippy_utils
crate。它包含许多低级任务的函数,例如查找符号和打印诊断消息,使编写检查规则变得更加容易。
我们要衷心感谢Clippy作者向Rust社区提供clippy_utils
crate。我们还要感谢Philipp Krones对本文早期版本提供的宝贵意见。