05-Lint 遍历
2 分钟阅读
译文 · 基于 Clippy Documentation
Lint 遍历
原文链接: https://doc.rust-lang.org/nightly/clippy/development/lint_passes.html
在实现新 lint 的逻辑之前,每位 Clippy 开发者都要做一个重要决定:使用
EarlyLintPass 还是 LateLintPass。
简而言之,LateLintPass 可访问类型与符号信息,而 EarlyLintPass 不能。若不需要类型信息,请使用 EarlyLintPass。
下面进一步说明这两个 trait。
EarlyLintPass
若仔细查看 EarlyLintPass 文档,会发现该 trait 的每个方法都使用
EarlyContext。EarlyContext 文档中写道:
在宏展开之后、降级为 HIR 之前,对 AST 进行 lint 检查的上下文。
可见,EarlyLintPass 仅在抽象语法树(AST)层面工作。
AST 在编译的词法分析与语法分析阶段生成。因此它不知道符号含义或类型信息;若 lint 只涉及语法相关问题,就应选择该 trait。
虽然 Clippy 并不特别关心 lint 速度,
EarlyLintPass 更快;若确定 lint 不需要类型信息,应优先选择它。
提醒:要为使用 EarlyLintPass 的 lint 生成样板代码,可运行:
| |
EarlyLintPass 示例
看以下代码:
| |
从 AST 角度看,两行在「语法」上都是正确的。
赋值用了 let 并以分号结尾;方法调用看起来也没问题。作为程序员我们可能已有疑问,但解析器可以接受。这就是我们所说的 EarlyLintPass 只处理 AST 层面语法。
另一种思路是定义新 Lint 一章提到的 foo_functions lint。
我们希望 foo_functions lint 检测名为 foo 的函数。
只检查函数名意味着只处理 AST,完全不必访问类型系统(类型系统正是 LateLintPass 的用武之地)。
LateLintPass
与 EarlyLintPass 相反,LateLintPass 包含类型信息。
若仔细查看 LateLintPass 文档,会发现该 trait 的每个方法都使用
LateContext。
在 LateContext 文档中,可以找到处理类型检查的方法,而 EarlyContext 中没有,例如:
LateLintPass 示例
看以下示例:
| |
从 AST 角度看,这两行语法正确:有赋值,并在某类型变量上调用方法。对解析器而言语法无误。
但若再深入类型信息层面,编译器会发现 OurUndefinedType 与 non_existing_method()
均未定义。
作为 Clippy 开发者,要访问这类类型信息,必须为我们的 lint 实现 LateLintPass。
浏览 Clippy 的 lint 时会发现,几乎所有 lint 都在 LateLintPass 中实现,因为我们常常不仅要检查语法问题,还要检查类型信息。
EarlyLintPass 的另一限制是:节点仅通过其在 AST 中的位置标识。这意味着不能简单地拿到某个 id 再请求对应节点。对大多数 lint 这没问题,但有些 lint 需要检查其他节点,在 HIR 层面更容易做到。此时 LateLintPass 是更好的选择。
提醒:要为使用 LateLintPass 的 lint 生成样板代码,可运行:
| |