发出 lint

一旦我们定义了一个 lint,编写了UI 测试,并为该 lint 选择了lint pass,我们就可以开始实现 lint 逻辑,以便我们可以发出它,并逐步实现一个按预期工作的 lint。

请注意,我们不会在本章中深入探讨 lint 逻辑的具体实现。我们将在后面的章节以及两个真实 Clippy lint 的示例中详细介绍。

要发出 lint,我们必须为我们声明的 lint 实现一个 pass(参见Lint Pass)。在此示例中,我们将实现一个“延迟” lint,因此请查看LateLintPass 文档,该文档提供了大量我们可以为我们的 lint 实现的方法。

#![allow(unused)] fn main() { pub trait LateLintPass<'tcx>: LintPass { // Trait methods } }

到目前为止,Clippy lint 最常用的方法是check_expr方法,这是因为 Rust 是一种表达式语言,而且通常情况下,我们想要处理的 lint 必须检查表达式。

注意:如果您不完全理解 Rust 中的表达式是什么,请查看官方文档中的表达式

其他常用的方法包括check_fn 方法check_item 方法

发出 lint

在我们实现的 trait 方法中,我们可以写下 lint 逻辑,并发出带有建议的 lint。

Clippy 的诊断提供了很多我们可以用来发出 lint 的诊断函数。请查看文档,选择最适合您的 lint 需求的函数。在 Clippy 存储库中您会遇到一些常见的函数,包括

#![allow(unused)] fn main() { impl<'tcx> LateLintPass<'tcx> for LintName { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint if some_lint_expr_logic(expr) { span_lint_and_help( cx, // < The context LINT_NAME, // < The name of the lint in ALL CAPS expr.span, // < The span to lint "message on why the lint is emitted", None, // < An optional help span (to highlight something in the lint) "message that provides a helpful suggestion", ); } } } }

注意:消息应该实事求是,避免使用大写字母和标点符号。如果需要多个句子,则消息应该拆分为错误 + 帮助/注释/建议消息。

建议:自动修复

有些 lint 知道如何更改以修复代码。例如,lint range_plus_one 会警告用户编写 x..y + 1 而不是使用包含范围x..=y)的情况。修复此代码会将 x..y + 1 表达式更改为 x..=y这就是建议的用武之地

建议是 lint 提供的用于修复它正在 lint 的问题的更改。输出如下所示(来自前面的示例)

error: an inclusive range would be more readable --> tests/ui/range_plus_minus_one.rs:37:14 | LL | for _ in 1..1 + 1 {} | ^^^^^^^^ help: use: `1..=1`

并非所有建议始终都是正确的,其中一些需要人工监督,这就是为什么我们有适用性

适用性表示对建议正确性的信心,有些建议始终是正确的(Applicability::MachineApplicable),但我们在谈论可能不正确的建议时会使用 Applicability::MaybeIncorrect 和其他。

示例

相同的 lint LINT_NAME 但会发出建议,如下所示

#![allow(unused)] fn main() { impl<'tcx> LateLintPass<'tcx> for LintName { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Imagine that `some_lint_expr_logic` checks for requirements for emitting the lint if some_lint_expr_logic(expr) { span_lint_and_sugg( // < Note this change cx, LINT_NAME, span, "message on why the lint is emitted", "use", format!("foo + {} * bar", snippet(cx, expr.span, "<default>")), // < Suggestion Applicability::MachineApplicable, ); } } } }

建议通常使用format! 宏来将旧值与新值进行插值。要获取代码片段,请使用 clippy_utils::source 中的 snippet* 函数之一。

如何在注释、帮助消息和建议之间进行选择

注释与主 lint 消息分开呈现,它们提供用户需要了解为什么激活了 lint 的有用信息。当附加到 span 时,它们最有用。

示例

注释

error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. --> tests/ui/drop_forget_ref.rs:10:5 | 10 | forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::forget-ref` implied by `-D warnings` note: argument has type &SomeStruct --> tests/ui/drop_forget_ref.rs:10:12 | 10 | forget(&SomeStruct); | ^^^^^^^^^^^

帮助消息

帮助消息专门用于帮助用户。这些用于无法提供特定的机器适用建议的情况。它们也可以附加到 span。

示例

error: constant division of 0.0 with 0.0 will always result in NaN --> tests/ui/zero_div_zero.rs:6:25 | 6 | let other_f64_nan = 0.0f64 / 0.0; | ^^^^^^^^^^^^ | = help: consider using `f64::NAN` if you would like a constant representing NaN

建议

建议是最有帮助的,它们是修复错误的代码更改。建议的魔力在于 rustfix 之类的工具可以检测到它们并自动修复您的代码。

示例

error: This `.fold` can be more succinctly expressed as `.any` --> tests/ui/methods.rs:390:13 | 390 | let _ = (0..3).fold(false, |acc, x| acc || x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)` |

代码片段

代码片段是源代码的一部分(作为字符串),它们通常使用snippet 函数提取。

例如,如果您想知道一个 item 的外观(并且您知道该 item 的 span),则可以使用 snippet(cx, span, "..")

最后:运行 UI 测试以发出 Lint

现在,如果我们运行我们的UI 测试,我们应该看到 Clippy 现在生成包含我们设计的 lint 消息的输出。

下一步是正确实现逻辑,这是我们将在下一章中介绍的细节。