Auto merge of #12648 - epage:lint, r=weihanglo

feat: Stabilize lints

Fixes #12115
This commit is contained in:
bors 2023-09-09 01:49:46 +00:00
commit 2fc85d15a5
7 changed files with 118 additions and 294 deletions

View File

@ -817,11 +817,10 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
}
// Try to inherit the workspace lints key if it exists.
if config.cli_unstable().lints
&& workspace_document
.get("workspace")
.and_then(|workspace| workspace.get("lints"))
.is_some()
if workspace_document
.get("workspace")
.and_then(|workspace| workspace.get("lints"))
.is_some()
{
let mut table = toml_edit::Table::new();
table["workspace"] = toml_edit::value(true);

View File

@ -339,7 +339,7 @@ pub struct TomlManifest {
patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
workspace: Option<TomlWorkspace>,
badges: Option<MaybeWorkspaceBtreeMap>,
lints: Option<toml::Value>,
lints: Option<MaybeWorkspaceLints>,
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
@ -1456,7 +1456,7 @@ pub struct TomlWorkspace {
// Properties that can be inherited by members.
package: Option<InheritableFields>,
dependencies: Option<BTreeMap<String, TomlDependency>>,
lints: Option<toml::Value>,
lints: Option<TomlLints>,
// Note that this field must come last due to the way toml serialization
// works which requires tables to be emitted after all values.
@ -1882,7 +1882,7 @@ impl TomlManifest {
let mut inheritable = toml_config.package.clone().unwrap_or_default();
inheritable.update_ws_path(package_root.to_path_buf());
inheritable.update_deps(toml_config.dependencies.clone());
let lints = parse_unstable_lints(toml_config.lints.clone(), config, &mut warnings)?;
let lints = toml_config.lints.clone();
let lints = verify_lints(lints)?;
inheritable.update_lints(lints);
if let Some(ws_deps) = &inheritable.dependencies {
@ -2143,10 +2143,11 @@ impl TomlManifest {
&inherit_cell,
)?;
let lints =
parse_unstable_lints::<MaybeWorkspaceLints>(me.lints.clone(), config, cx.warnings)?
.map(|mw| mw.resolve(|| inherit()?.lints()))
.transpose()?;
let lints = me
.lints
.clone()
.map(|mw| mw.resolve(|| inherit()?.lints()))
.transpose()?;
let lints = verify_lints(lints)?;
let default = TomlLints::default();
let rustflags = lints_to_rustflags(lints.as_ref().unwrap_or(&default));
@ -2447,7 +2448,10 @@ impl TomlManifest {
.badges
.as_ref()
.map(|_| MaybeWorkspace::Defined(metadata.badges.clone())),
lints: lints.map(|lints| toml::Value::try_from(lints).unwrap()),
lints: lints.map(|lints| MaybeWorkspaceLints {
workspace: false,
lints,
}),
};
let mut manifest = Manifest::new(
summary,
@ -2579,7 +2583,7 @@ impl TomlManifest {
let mut inheritable = toml_config.package.clone().unwrap_or_default();
inheritable.update_ws_path(root.to_path_buf());
inheritable.update_deps(toml_config.dependencies.clone());
let lints = parse_unstable_lints(toml_config.lints.clone(), config, &mut warnings)?;
let lints = toml_config.lints.clone();
let lints = verify_lints(lints)?;
inheritable.update_lints(lints);
let ws_root_config = WorkspaceRootConfig::new(
@ -2726,55 +2730,6 @@ impl TomlManifest {
}
}
fn parse_unstable_lints<T: Deserialize<'static>>(
lints: Option<toml::Value>,
config: &Config,
warnings: &mut Vec<String>,
) -> CargoResult<Option<T>> {
let Some(lints) = lints else {
return Ok(None);
};
if !config.cli_unstable().lints {
warn_for_lint_feature(config, warnings);
return Ok(None);
}
lints.try_into().map(Some).map_err(|err| err.into())
}
fn warn_for_lint_feature(config: &Config, warnings: &mut Vec<String>) {
use std::fmt::Write as _;
let key_name = "lints";
let feature_name = "lints";
let mut message = String::new();
let _ = write!(
message,
"unused manifest key `{key_name}` (may be supported in a future version)"
);
if config.nightly_features_allowed {
let _ = write!(
message,
"
consider passing `-Z{feature_name}` to enable this feature."
);
} else {
let _ = write!(
message,
"
this Cargo does not support nightly features, but if you
switch to nightly channel you can pass
`-Z{feature_name}` to enable this feature.",
);
}
warnings.push(message);
}
fn verify_lints(lints: Option<TomlLints>) -> CargoResult<Option<TomlLints>> {
let Some(lints) = lints else {
return Ok(None);

View File

@ -48,6 +48,7 @@ Every manifest file consists of the following sections:
* [`[target]`](specifying-dependencies.md#platform-specific-dependencies) --- Platform-specific dependencies.
* [`[badges]`](#the-badges-section) --- Badges to display on a registry.
* [`[features]`](features.md) --- Conditional compilation features.
* [`[lints]`](#the-lints-section) --- Configure linters for this package.
* [`[patch]`](overriding-dependencies.md#the-patch-section) --- Override dependencies.
* [`[replace]`](overriding-dependencies.md#the-replace-section) --- Override dependencies (deprecated).
* [`[profile]`](profiles.md) --- Compiler settings and optimizations.
@ -530,6 +531,46 @@ both `src/bin/a.rs` and `src/bin/b.rs`:
default-run = "a"
```
#### The `lints` section
Override the default level of lints from different tools by assigning them to a new level in a
table, for example:
```toml
[lints.rust]
unsafe_code = "forbid"
```
This is short-hand for:
```toml
[lints.rust]
unsafe_code = { level = "forbid", priority = 0 }
```
`level` corresponds to the lint levels in `rustc`:
- `forbid`
- `deny`
- `warn`
- `allow`
`priority` is a signed integer that controls which lints or lint groups override other lint groups:
- lower (particularly negative) numbers have lower priority, being overridden
by higher numbers, and show up first on the command-line to tools like
`rustc`
To know which table under `[lints]` a particular lint belongs under, it is the part before `::` in the lint
name. If there isn't a `::`, then the tool is `rust`. For example a warning
about `unsafe_code` would be `lints.rust.unsafe_code` but a lint about
`clippy::enum_glob_use` would be `lints.clippy.enum_glob_use`.
For example:
```toml
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
enum_glob_use = "deny"
```
## The `[badges]` section
The `[badges]` section is for specifying status badges that can be displayed

View File

@ -93,7 +93,6 @@ For the latest nightly, see the [nightly version] of this page.
* [codegen-backend](#codegen-backend) --- Select the codegen backend used by rustc.
* [per-package-target](#per-package-target) --- Sets the `--target` to use for each individual package.
* [artifact dependencies](#artifact-dependencies) --- Allow build artifacts to be included into other build artifacts and build them for different targets.
* [`[lints]`](#lints) --- Configure lint levels for various linter tools.
* Information and metadata
* [Build-plan](#build-plan) --- Emits JSON information on which commands will be run.
* [unit-graph](#unit-graph) --- Emits JSON for Cargo's internal graph structure.
@ -1571,100 +1570,8 @@ Differences between `cargo run --manifest-path <path>` and `cargo <path>`
- `cargo <path>` runs with the config for `<path>` and not the current dir, more like `cargo install --path <path>`
- `cargo <path>` is at a verbosity level below the normal default. Pass `-v` to get normal output.
## `[lints]`
* Tracking Issue: [#12115](https://github.com/rust-lang/cargo/issues/12115)
A new `lints` table would be added to configure lints:
```toml
[lints.rust]
unsafe = "forbid"
```
and `cargo` would pass these along as flags to `rustc`, `clippy`, or other lint tools when `-Zlints` is used.
This would work with
[RFC 2906 `workspace-deduplicate`](https://rust-lang.github.io/rfcs/2906-cargo-workspace-deduplicate.html):
```toml
[lints]
workspace = true
[workspace.lints.rust]
unsafe = "forbid"
```
### Documentation Updates
#### The `lints` section
*as a new ["Manifest Format" entry](./manifest.html#the-manifest-format)*
Override the default level of lints from different tools by assigning them to a new level in a
table, for example:
```toml
[lints.rust]
unsafe = "forbid"
```
This is short-hand for:
```toml
[lints.rust]
unsafe = { level = "forbid", priority = 0 }
```
`level` corresponds to the lint levels in `rustc`:
- `forbid`
- `deny`
- `warn`
- `allow`
`priority` is a signed integer that controls which lints or lint groups override other lint groups:
- lower (particularly negative) numbers have lower priority, being overridden
by higher numbers, and show up first on the command-line to tools like
`rustc`
To know which table under `[lints]` a particular lint belongs under, it is the part before `::` in the lint
name. If there isn't a `::`, then the tool is `rust`. For example a warning
about `unsafe` would be `lints.rust.unsafe` but a lint about
`clippy::enum_glob_use` would be `lints.clippy.enum_glob_use`.
For example:
```toml
[lints.rust]
unsafe = "forbid"
[lints.clippy]
enum_glob_use = "deny"
```
#### The `lints` table
*as a new [`[workspace]` entry](./workspaces.html#the-workspace-section)*
The `workspace.lints` table is where you define lint configuration to be inherited by members of a workspace.
Specifying a workspace lint configuration is similar to package lints.
Example:
```toml
# [PROJECT_DIR]/Cargo.toml
[workspace]
members = ["crates/*"]
[workspace.lints.rust]
unsafe = "forbid"
```
```toml
# [PROJECT_DIR]/crates/bar/Cargo.toml
[package]
name = "bar"
version = "0.1.0"
[lints]
workspace = true
```
# Stabilized and removed features
## Compile progress
@ -1886,3 +1793,8 @@ for more information about the working directory for compiling and running tests
The `--keep-going` option has been stabilized in the 1.74 release. See the
[`--keep-going` flag](../commands/cargo-build.html#option-cargo-build---keep-going)
in `cargo build` as an example for more details.
## `[lints]`
[`[lints]`](manifest.html#the-lints-section) (enabled via `-Zlints`) has been stabilized in the 1.74 release.

View File

@ -24,6 +24,7 @@ In the `Cargo.toml`, the `[workspace]` table supports the following sections:
* [`default-members`](#the-default-members-field) --- Packages to operate on when a specific package wasn't selected.
* [`package`](#the-package-table) --- Keys for inheriting in packages.
* [`dependencies`](#the-dependencies-table) --- Keys for inheriting in package dependencies.
* [`lints`](#the-lints-table) --- Keys for inheriting in package lints.
* [`metadata`](#the-metadata-table) --- Extra settings for external tools.
* [`[patch]`](overriding-dependencies.md#the-patch-section) --- Override dependencies.
* [`[replace]`](overriding-dependencies.md#the-replace-section) --- Override dependencies (deprecated).
@ -222,6 +223,33 @@ cc.workspace = true
rand.workspace = true
```
## The `lints` table
The `workspace.lints` table is where you define lint configuration to be inherited by members of a workspace.
Specifying a workspace lint configuration is similar to package lints.
Example:
```toml
# [PROJECT_DIR]/Cargo.toml
[workspace]
members = ["crates/*"]
[workspace.lints.rust]
unsafe_code = "forbid"
```
```toml
# [PROJECT_DIR]/crates/bar/Cargo.toml
[package]
name = "bar"
version = "0.1.0"
[lints]
workspace = true
```
## The `metadata` table
The `workspace.metadata` table is ignored by Cargo and will not be warned

View File

@ -1,7 +1,6 @@
use cargo_test_support::compare::assert_ui;
use cargo_test_support::curr_dir;
use cargo_test_support::CargoCommand;
use cargo_test_support::ChannelChanger;
use cargo_test_support::Project;
#[cargo_test]
@ -12,9 +11,8 @@ fn case() {
snapbox::cmd::Command::cargo_ui()
.arg("new")
.args(["crates/foo", "-Zlints"])
.args(["crates/foo"])
.current_dir(cwd)
.masquerade_as_nightly_cargo(&["lints"])
.assert()
.success()
.stdout_matches_path(curr_dir!().join("stdout.log"))

View File

@ -3,72 +3,6 @@
use cargo_test_support::project;
use cargo_test_support::registry::Package;
#[cargo_test]
fn package_requires_option() {
let foo = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[lints.rust]
unsafe_code = "forbid"
"#,
)
.file("src/lib.rs", "")
.build();
foo.cargo("check")
.with_stderr(
"\
warning: unused manifest key `lints` (may be supported in a future version)
this Cargo does not support nightly features, but if you
switch to nightly channel you can pass
`-Zlints` to enable this feature.
[CHECKING] [..]
[FINISHED] [..]
",
)
.run();
}
#[cargo_test]
fn workspace_requires_option() {
let foo = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
authors = []
[workspace.lints.rust]
unsafe_code = "forbid"
"#,
)
.file("src/lib.rs", "")
.build();
foo.cargo("check")
.with_stderr(
"\
warning: [CWD]/Cargo.toml: unused manifest key `lints` (may be supported in a future version)
this Cargo does not support nightly features, but if you
switch to nightly channel you can pass
`-Zlints` to enable this feature.
[CHECKING] [..]
[FINISHED] [..]
",
)
.run();
}
#[cargo_test]
fn dependency_warning_ignored() {
let foo = project()
@ -133,45 +67,16 @@ fn malformed_on_stable() {
.build();
foo.cargo("check")
.with_stderr(
"\
warning: unused manifest key `lints` (may be supported in a future version)
this Cargo does not support nightly features, but if you
switch to nightly channel you can pass
`-Zlints` to enable this feature.
[CHECKING] [..]
[FINISHED] [..]
",
)
.run();
}
#[cargo_test]
fn malformed_on_nightly() {
let foo = project()
.file(
"Cargo.toml",
r#"
lints = 20
[package]
name = "foo"
version = "0.0.1"
authors = []
"#,
)
.file("src/lib.rs", "")
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
.with_status(101)
.with_stderr(
"\
error: failed to parse manifest[..]
Caused by:
TOML parse error at line 2, column 25
|
2 | lints = 20
| ^^
invalid type: integer `20`, expected a lints table
",
)
@ -196,8 +101,7 @@ fn fail_on_invalid_tool() {
.file("src/lib.rs", "")
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr(
"\
@ -227,16 +131,18 @@ fn invalid_type_in_lint_value() {
.file("src/lib.rs", "")
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr(
"\
error: failed to parse manifest at `[..]/Cargo.toml`
Caused by:
TOML parse error at line 7, column 36
|
7 | rust-2018-idioms = -1
| ^^
invalid type: integer `-1`, expected a string or map
in `rust.rust-2018-idioms`
",
)
.run();
@ -260,8 +166,7 @@ fn fail_on_tool_injection() {
.file("src/lib.rs", "")
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr(
"\
@ -292,8 +197,7 @@ fn fail_on_redundant_tool() {
.file("src/lib.rs", "")
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr(
"\
@ -324,8 +228,7 @@ fn fail_on_conflicting_tool() {
.file("src/lib.rs", "")
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr(
"\
@ -363,8 +266,7 @@ pub fn foo(num: i32) -> u32 {
)
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr_contains(
"\
@ -402,8 +304,7 @@ pub fn foo(num: i32) -> u32 {
)
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr_contains(
"\
@ -443,8 +344,7 @@ pub fn foo(num: i32) -> u32 {
)
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr(
"\
@ -484,9 +384,8 @@ pub fn foo(num: i32) -> u32 {
)
.build();
foo.cargo("check -Zlints")
foo.cargo("check")
.arg("-v") // Show order of rustflags on failure
.masquerade_as_nightly_cargo(&["lints"])
.run();
}
@ -515,10 +414,9 @@ pub fn foo(num: i32) -> u32 {
)
.build();
foo.cargo("check -Zlints")
foo.cargo("check")
.arg("-v") // Show order of rustflags on failure
.env("RUSTFLAGS", "-Aunsafe_code")
.masquerade_as_nightly_cargo(&["lints"])
.run();
}
@ -551,9 +449,9 @@ pub fn foo(num: i32) -> u32 {
)
.build();
foo.cargo("check -Zlints")
foo.cargo("check")
.arg("-v") // Show order of rustflags on failure
.masquerade_as_nightly_cargo(&["lints", "profile-rustflags"])
.masquerade_as_nightly_cargo(&["profile-rustflags"])
.run();
}
@ -588,9 +486,8 @@ pub fn foo(num: i32) -> u32 {
)
.build();
foo.cargo("check -Zlints")
foo.cargo("check")
.arg("-v") // Show order of rustflags on failure
.masquerade_as_nightly_cargo(&["lints"])
.run();
}
@ -628,8 +525,7 @@ pub fn foo() -> u32 {
)
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_status(101)
.with_stderr_contains(
"\
@ -673,9 +569,7 @@ pub fn foo() -> u32 {
)
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
.run();
foo.cargo("check").run();
}
#[cargo_test]
@ -703,8 +597,7 @@ pub fn foo() -> u32 {
)
.build();
foo.cargo("doc -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("doc")
.with_status(101)
.with_stderr_contains(
"\
@ -747,8 +640,7 @@ pub const Ĕ: i32 = 2;
)
.build();
foo.cargo("check -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("check")
.with_stderr(
"\
[CHECKING] foo v0.0.1 ([CWD])
@ -757,8 +649,7 @@ pub const Ĕ: i32 = 2;
)
.run();
foo.cargo("test --doc -Zlints")
.masquerade_as_nightly_cargo(&["lints"])
foo.cargo("test --doc")
.with_stderr(
"\
[COMPILING] foo v0.0.1 ([CWD])