发出 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 存储库中您会遇到一些常见的函数,包括
span_lint
:发出一个 lint,而不提供任何其他信息span_lint_and_note
:发出一个 lint 并添加一个注释span_lint_and_help
:发出一个 lint 并提供有用的消息span_lint_and_sugg
:发出一个 lint 并提供修复代码的建议span_lint_and_then
:类似于span_lint
,但允许进行大量的输出自定义。
#![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 消息的输出。
下一步是正确实现逻辑,这是我们将在下一章中介绍的细节。