发出 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 消息的输出。

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