Lint 级别

rustc 中,lint 被分为六个级别

  1. allow(允许)
  2. expect(期望)
  3. warn(警告)
  4. force-warn(强制警告)
  5. deny(拒绝)
  6. forbid(禁止)

每个 lint 都有一个默认级别(在本章后面的 lint 列表中会解释),并且编译器也有一个默认的警告级别。首先,让我们解释一下这些级别的含义,然后我们将讨论配置。

allow(允许)

这些 lint 存在,但默认情况下不执行任何操作。例如,考虑以下源代码

pub fn foo() {}

编译此文件不会产生任何警告

$ rustc lib.rs --crate-type=lib
$

但是此代码违反了 missing_docs lint。

这些 lint 的存在主要是为了通过配置手动启用,我们将在本节的后面讨论。

expect(期望)

有时,抑制 lint 可能很有用,但同时要确保相关代码仍然发出它们。“expect” 级别正是这样做的。如果未发出相关的 lint,则 expect 属性会触发 unfulfilled_lint_expectation lint,通知你该期望不再满足。

fn main() {
    #[expect(unused_variables)]
    let unused = "Everyone ignores me";

    #[expect(unused_variables)] // `unused_variables` lint is not emitted
    let used = "I'm useful";    // the expectation is therefore unfulfilled
    println!("The `used` value is equal to: {:?}", used);
}

这将产生以下警告

warning: this lint expectation is unfulfilled
 --> src/main.rs:7:14
  |
7 |     #[expect(unused_variables)]
  |              ^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unfulfilled_lint_expectations)]` on by default

此级别只能通过 #[expect] 属性定义,没有等效的标志。具有特殊 “force-warn” 级别的 lint 仍将照常发出。

warn(警告)

如果违反 lint,“warn” lint 级别将产生警告。例如,此代码违反了 unused_variables lint

pub fn foo() {
    let x = 5;
}

这将产生此警告

$ rustc lib.rs --crate-type=lib
warning: unused variable: `x`
 --> lib.rs:2:9
  |
2 |     let x = 5;
  |         ^
  |
  = note: `#[warn(unused_variables)]` on by default
  = note: to avoid this warning, consider using `_x` instead

force-warn(强制警告)

“force-warn” 是一种特殊的 lint 级别。它与 “warn” 级别相同,因为此级别的 lint 会产生警告,但与 “warn” 级别不同,“force-warn” 级别无法被覆盖。如果一个 lint 设置为 “force-warn”,则保证会发出警告:不多也不少。即使通过 cap-lints 限制了整体 lint 级别也是如此。

deny(拒绝)

如果违反 lint,“deny” lint 会产生错误。例如,此代码遇到了 exceeding_bitshifts lint。

fn main() {
    100u8 << 10;
}
$ rustc main.rs
error: bitshift exceeds the type's number of bits
 --> main.rs:2:13
  |
2 |     100u8 << 10;
  |     ^^^^^^^^^^^
  |
  = note: `#[deny(exceeding_bitshifts)]` on by default

lint 的错误与常规错误之间有什么区别?lint 可以通过级别进行配置,因此与 “allow” lint 类似,默认情况下为 “deny” 的警告允许你允许它们。同样,你可能希望将默认 warn 的 lint 设置为产生错误。此 lint 级别为你提供了该功能。

forbid(禁止)

“forbid” 是一种特殊的 lint 级别,对于 “deny” 的作用与 “force-warn” 对于 “warn” 的作用相同。它与 “deny” 级别相同,此级别的 lint 会产生错误,但与 “deny” 级别不同,“forbid” 级别不能被覆盖为低于错误的任何级别。但是,lint 级别仍然可以使用 --cap-lints 进行限制(请参阅下文),因此 rustc --cap-lints warn 将使设置为 “forbid” 的 lint 仅发出警告。

配置警告级别

还记得我们在 “allow” lint 级别中的 missing_docs 示例吗?

$ cat lib.rs
pub fn foo() {}
$ rustc lib.rs --crate-type=lib
$

我们可以使用编译器标志以及源代码中的属性来配置此 lint 以更高的级别运行。

你还可以“限制” lint,以便编译器可以选择忽略某些 lint 级别。我们最后再讨论这一点。

通过编译器标志

-A-W--force-warn -D-F 标志使你可以将一个或多个 lint 转换为允许、警告、强制警告、拒绝或禁止级别,如下所示

$ rustc lib.rs --crate-type=lib -W missing-docs
warning: missing documentation for crate
 --> lib.rs:1:1
  |
1 | pub fn foo() {}
  | ^^^^^^^^^^^^
  |
  = note: requested on the command line with `-W missing-docs`

warning: missing documentation for a function
 --> lib.rs:1:1
  |
1 | pub fn foo() {}
  | ^^^^^^^^^^^^
$ rustc lib.rs --crate-type=lib -D missing-docs
error: missing documentation for crate
 --> lib.rs:1:1
  |
1 | pub fn foo() {}
  | ^^^^^^^^^^^^
  |
  = note: requested on the command line with `-D missing-docs`

error: missing documentation for a function
 --> lib.rs:1:1
  |
1 | pub fn foo() {}
  | ^^^^^^^^^^^^

error: aborting due to 2 previous errors

你还可以多次传递每个标志来更改多个 lint

$ rustc lib.rs --crate-type=lib -D missing-docs -D unused-variables

当然,你可以将这五个标志混合在一起

$ rustc lib.rs --crate-type=lib -D missing-docs -A unused-variables

这些命令行参数的顺序将被考虑在内。以下允许 unused-variables lint,因为它是该 lint 的最后一个参数

$ rustc lib.rs --crate-type=lib -D unused-variables -A unused-variables

你可以通过覆盖一组 lint 中一个特定 lint 的级别来利用此行为。以下示例拒绝 unused 组中的所有 lint,但显式允许该组中的 unused-variables lint(无论顺序如何,forbid 仍然胜过一切)

$ rustc lib.rs --crate-type=lib -D unused -A unused-variables

由于 force-warnforbid 无法被覆盖,因此设置其中一个将阻止同一 lint 的任何后续级别生效。

通过属性

你还可以使用 crate 范围的属性修改 lint 级别

$ cat lib.rs
#![warn(missing_docs)]

pub fn foo() {}
$ rustc lib.rs --crate-type=lib
warning: missing documentation for crate
 --> lib.rs:1:1
  |
1 | / #![warn(missing_docs)]
2 | |
3 | | pub fn foo() {}
  | |_______________^
  |
note: lint level defined here
 --> lib.rs:1:9
  |
1 | #![warn(missing_docs)]
  |         ^^^^^^^^^^^^

warning: missing documentation for a function
 --> lib.rs:3:1
  |
3 | pub fn foo() {}
  | ^^^^^^^^^^^^

warnallowdenyforbid 都以这种方式工作。无法使用属性将 lint 设置为 force-warn

你还可以在每个属性中传入多个 lint

#![warn(missing_docs, unused_variables)]

pub fn foo() {}

并一起使用多个属性

#![warn(missing_docs)]
#![deny(unused_variables)]

pub fn foo() {}

所有 lint 属性都支持附加的 reason 参数,以提供添加特定属性的原因。如果 lint 以定义的级别发出,则此原因将作为 lint 消息的一部分显示。

use std::path::PathBuf;
pub fn get_path() -> PathBuf {
    #[allow(unused_mut, reason = "this is only modified on some platforms")]
    let mut file_name = PathBuf::from("git");
    #[cfg(target_os = "windows")]
    file_name.set_extension("exe");
    file_name
}

限制 lint

rustc 支持一个标志 --cap-lints LEVEL,用于设置 “lint 限制级别”。这是所有 lint 的最大级别。因此,例如,如果我们从上面的 “deny” lint 级别获取代码示例

fn main() {
    100u8 << 10;
}

然后我们编译它,将 lint 限制为 warn

$ rustc lib.rs --cap-lints warn
warning: bitshift exceeds the type's number of bits
 --> lib.rs:2:5
  |
2 |     100u8 << 10;
  |     ^^^^^^^^^^^
  |
  = note: `#[warn(exceeding_bitshifts)]` on by default

warning: this expression will panic at run-time
 --> lib.rs:2:5
  |
2 |     100u8 << 10;
  |     ^^^^^^^^^^^ attempt to shift left with overflow

现在它只发出警告,而不是错误。我们可以更进一步并允许所有 lint

$ rustc lib.rs --cap-lints allow
$

Cargo 大量使用了此功能;它将在编译你的依赖项时传递 --cap-lints allow,这样如果它们有任何警告,它们就不会污染你的构建输出。