定义新的 Lint

定义新 Lint 的第一步是在 Clippy 代码库中定义和注册 Lint。我们可以使用 Clippy 开发工具来处理这一步,因为设置 Lint 涉及到一些样板代码。

Lint 类型

Lint 类型是指你的 Lint 所关注的项目和表达式的类别。

在编写本文档更新时,除了 clippy_lints/src/ 下的众多独立 Lint 外,还有 12 种 类型 的 Lint

  • cargo
  • 类型转换
  • 函数
  • 循环
  • 匹配
  • 方法
  • 其他早期
  • 运算符
  • 强制类型转换
  • 类型
  • 单元类型
  • utils / internal (Clippy 内部 Lint)

这些类型将具有一些共同行为的 Lint 分组在一起。例如,functions 将处理 Rust 中函数某些方面的 Lint 分组在一起,例如定义、签名和属性。

有关更多信息,请随时将任何类别下的 Lint 文件与 所有 Clippy Lint 进行比较,或咨询维护人员之一。

Lint 名称

一个好的 Lint 名称很重要,请务必查看 Lint 命名指南。不用担心,如果 Lint 名称不合适,Clippy 团队成员会在 PR 过程中提醒您。


我们将把检测名为 "foo" 的函数的示例 Lint 命名为 foo_functions。查看 Lint 命名指南,了解为什么这个名称有意义。

添加和注册 Lint

现在选择了名称,我们将 foo_functions 注册为代码库中的 Lint。有两种方法可以注册 Lint。

独立

如果您认为此新 Lint 是独立的 Lint(不属于任何特定的 类型,如 functionsloops),则可以在您的 Clippy 项目中运行以下命令

$ cargo dev new_lint --name=lint_name --pass=late --category=pedantic

这里有两点需要注意

  1. --pass: 我们在此命令中设置 --pass=late 以进行后期 Lint 传递。另一种选择是 early Lint 传递。我们将在 Lint 传递章节中讨论此区别。
  2. --category: 如果未提供,则此新 Lint 的 category 将默认为 nursery

cargo dev new_lint 命令将创建一个新文件:clippy_lints/src/foo_functions.rs 以及 注册 Lint

总的来说,您应该注意到以下文件被修改或创建

$ git status
On branch foo_functions
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CHANGELOG.md
	modified:   clippy_lints/src/lib.register_lints.rs
	modified:   clippy_lints/src/lib.register_pedantic.rs
	modified:   clippy_lints/src/lib.rs

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	clippy_lints/src/foo_functions.rs
	tests/ui/foo_functions.rs

特定类型

注意: Lint 类型在 "Lint 类型" 部分列出

如果您认为此新 Lint 属于特定类型的 Lint,则可以使用 --type 选项运行 cargo dev new_lint

由于我们的 foo_functions Lint 与函数调用相关,因此有人可能会认为我们应该将其放入一组检测函数某些行为的 Lint 中,我们可以将其放入 functions 组。

让我们在您的 Clippy 项目中运行以下命令

$ cargo dev new_lint --name=foo_functions --type=functions --category=pedantic

此命令将创建(除其他外)一个新文件:clippy_lints/src/{type}/foo_functions.rs。在我们的例子中,路径将是 clippy_lints/src/functions/foo_functions.rs

请注意,此命令具有 --type 标志而不是 --pass。与独立定义不同,此 Lint 不会以传统方式注册。相反,您将从类型 Lint 传递(在 clippy_lints/src/{type}/mod.rs 中找到)中调用您的 Lint。

type 只是 clippy_lints/src 中的一个目录的名称,例如示例命令中的 functions。Clippy 将一些具有共同行为的 Lint 分组在一起,因此如果您的 Lint 属于其中一种,最好将其添加到该类型中。

总的来说,您应该注意到以下文件被修改或创建

$ git status
On branch foo_functions
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CHANGELOG.md
	modified:   clippy_lints/src/declared_lints.rs
	modified:   clippy_lints/src/functions/mod.rs

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	clippy_lints/src/functions/foo_functions.rs
	tests/ui/foo_functions.rs

define_clippy_lints

cargo dev new_lint 之后,您应该看到一个名为 define_clippy_lints 的宏。如果您定义了一个独立的 Lint,它将在同一个文件中,如果您定义了一个类型特定的 Lint,它将在 mod.rs 中。

宏看起来像这样

#![allow(unused)]
fn main() {
declare_clippy_lint! {
    /// ### What it does
    ///
    /// // Describe here what does the lint do.
    ///
    /// Triggers when detects...
    ///
    /// ### Why is this bad?
    ///
    /// // Describe why this pattern would be bad
    ///
    /// It can lead to...
    ///
    /// ### Example
    /// ```rust
    /// // example code where Clippy issues a warning
    /// ```
    /// Use instead:
    /// ```rust
    /// // example code which does not raise Clippy warning
    /// ```
    #[clippy::version = "1.70.0"] // <- In which version was this implemented, keep it up to date!
    pub LINT_NAME, // <- The lint name IN_ALL_CAPS
    pedantic, // <- The lint group
    "default lint description" // <- A lint description, e.g. "A function has an unit return type."
}
}

Lint 注册

如果我们为新的 Lint 运行 cargo dev new_lint 命令,则该 Lint 将自动注册,无需执行更多操作。

但是,有时我们可能想手动声明新的 Lint。在这种情况下,我们会之后使用 cargo dev update_lints 命令。

当手动声明 Lint 时,我们可能需要在 clippy_lints/src/lib.rs 中的 register_lints 函数中手动注册 Lint 传递

#![allow(unused)]
fn main() {
store.register_late_pass(|_| Box::new(foo_functions::FooFunctions));
}

您可能已经猜到了,有后期,就有早期:在 Clippy 中,也有一个 register_early_pass 方法。有关早期与后期传递的更多信息,请参阅 Lint 传递章节。

如果没有调用 register_early_passregister_late_pass 之一,则不会运行有问题的 Lint 传递。