diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 0799ef9d3f1..796a1f93be6 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -2690,7 +2690,23 @@ supported tools: {}", if tool == "cargo" && !gctx.cli_unstable().cargo_lints { warn_for_cargo_lint_feature(gctx, warnings); } + let mut seen_normalized: HashMap = HashMap::new(); for (name, config) in lints { + let normalized = name.replace('-', "_"); + if name.contains('-') { + warnings.push(format!( + "`lints.{tool}.{name}` is deprecated in favor of \ + `lints.{tool}.{normalized}` and will not work in a \ + future edition" + )); + } + if let Some(existing) = seen_normalized.get(&normalized) { + anyhow::bail!( + "duplicate lint `{existing}` in `[lints.{tool}]`, \ + conflicts with `{name}`" + ); + } + seen_normalized.insert(normalized.clone(), name.to_string()); if let Some((prefix, suffix)) = name.split_once("::") { if tool == prefix { anyhow::bail!( @@ -2772,10 +2788,11 @@ fn lints_to_rustflags(lints: &manifest::TomlLints) -> CargoResult> { manifest::TomlLintLevel::Allow => "--allow", }; + let normalized_name = name.replace('-', "_"); let option = if tool == "rust" { - format!("{flag}={name}") + format!("{flag}={normalized_name}") } else { - format!("{flag}={tool}::{name}") + format!("{flag}={tool}::{normalized_name}") }; ( config.priority(), diff --git a/tests/testsuite/lints/mod.rs b/tests/testsuite/lints/mod.rs index ac31971c995..d3eb7fc1923 100644 --- a/tests/testsuite/lints/mod.rs +++ b/tests/testsuite/lints/mod.rs @@ -47,6 +47,7 @@ im-a-teapot = "warn" foo.cargo("fetch -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .with_stderr_data(str![[r#" +[WARNING] Cargo.toml: `lints.cargo.im-a-teapot` is deprecated in favor of `lints.cargo.im_a_teapot` and will not work in a future edition [WARNING] unknown lint: `im-a-teapot` --> Cargo.toml:12:1 | @@ -55,7 +56,7 @@ im-a-teapot = "warn" | = [NOTE] `cargo::unknown_lints` is set to `warn` by default = [HELP] there is a lint with a similar name: `im_a_teapot` -[WARNING] `foo` (manifest) generated 1 warning +[WARNING] `foo` (manifest) generated 2 warnings "#]]) .run(); diff --git a/tests/testsuite/lints/unknown_lints.rs b/tests/testsuite/lints/unknown_lints.rs index 3a74547122c..cc93680a78d 100644 --- a/tests/testsuite/lints/unknown_lints.rs +++ b/tests/testsuite/lints/unknown_lints.rs @@ -26,6 +26,7 @@ this-lint-does-not-exist = "warn" p.cargo("fetch -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_stderr_data(str![[r#" +[WARNING] Cargo.toml: `lints.cargo.this-lint-does-not-exist` is deprecated in favor of `lints.cargo.this_lint_does_not_exist` and will not work in a future edition [WARNING] unknown lint: `this-lint-does-not-exist` --> Cargo.toml:11:1 | @@ -33,7 +34,7 @@ this-lint-does-not-exist = "warn" | ^^^^^^^^^^^^^^^^^^^^^^^^ | = [NOTE] `cargo::unknown_lints` is set to `warn` in `[lints]` -[WARNING] `foo` (manifest) generated 1 warning +[WARNING] `foo` (manifest) generated 2 warnings "#]]) .run(); @@ -73,6 +74,7 @@ workspace = true p.cargo("fetch -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_stderr_data(str![[r#" +[WARNING] Cargo.toml: `lints.cargo.this-lint-does-not-exist` is deprecated in favor of `lints.cargo.this_lint_does_not_exist` and will not work in a future edition [WARNING] unknown lint: `this-lint-does-not-exist` --> Cargo.toml:8:1 | @@ -80,7 +82,9 @@ workspace = true | ^^^^^^^^^^^^^^^^^^^^^^^^ | = [NOTE] `cargo::unknown_lints` is set to `warn` in `[lints]` -[WARNING] workspace (manifest) generated 1 warning +[WARNING] workspace (manifest) generated 2 warnings +[WARNING] foo/Cargo.toml: `lints.cargo.this-lint-does-not-exist` is deprecated in favor of `lints.cargo.this_lint_does_not_exist` and will not work in a future edition +[WARNING] `foo` (manifest) generated 1 warning "#]]) .run(); @@ -117,6 +121,7 @@ authors = [] p.cargo("fetch -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints"]) .with_stderr_data(str![[r#" +[WARNING] Cargo.toml: `lints.cargo.this-lint-does-not-exist` is deprecated in favor of `lints.cargo.this_lint_does_not_exist` and will not work in a future edition [WARNING] unknown lint: `this-lint-does-not-exist` --> Cargo.toml:8:1 | @@ -124,7 +129,7 @@ authors = [] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = [NOTE] `cargo::unknown_lints` is set to `warn` in `[lints]` -[WARNING] workspace (manifest) generated 1 warning +[WARNING] workspace (manifest) generated 2 warnings [WARNING] missing `[lints]` to inherit `[workspace.lints]` --> foo/Cargo.toml = [NOTE] `cargo::missing_lints_inheritance` is set to `warn` by default diff --git a/tests/testsuite/lints_table.rs b/tests/testsuite/lints_table.rs index 05edfec1342..a578116bbe8 100644 --- a/tests/testsuite/lints_table.rs +++ b/tests/testsuite/lints_table.rs @@ -166,9 +166,11 @@ fn warn_on_unused_key() { foo.cargo("check") .with_stderr_data(str![[r#" +[WARNING] Cargo.toml: `lints.rust.rust-2018-idioms` is deprecated in favor of `lints.rust.rust_2018_idioms` and will not work in a future edition [WARNING] Cargo.toml: unused manifest key: `lints.rust.rust-2018-idioms.unused` +[WARNING] Cargo.toml: `lints.rust.rust-2018-idioms` is deprecated in favor of `lints.rust.rust_2018_idioms` and will not work in a future edition [WARNING] Cargo.toml: unused manifest key: `lints.rust.rust-2018-idioms.unused` -[WARNING] `foo` (manifest) generated 2 warnings +[WARNING] `foo` (manifest) generated 4 warnings [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -720,6 +722,8 @@ pub const Ĕ: i32 = 2; foo.cargo("check") .with_stderr_data(str![[r#" +[WARNING] Cargo.toml: `lints.rust.confusable-idents` is deprecated in favor of `lints.rust.confusable_idents` and will not work in a future edition +[WARNING] `foo` (manifest) generated 1 warning [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -728,6 +732,8 @@ pub const Ĕ: i32 = 2; foo.cargo("test --doc") .with_stderr_data(str![[r#" +[WARNING] Cargo.toml: `lints.rust.confusable-idents` is deprecated in favor of `lints.rust.confusable_idents` and will not work in a future edition +[WARNING] `foo` (manifest) generated 1 warning [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCTEST] foo @@ -843,3 +849,63 @@ im_a_teapot = "warn" "#]]) .run(); } + +#[cargo_test] +fn hyphen_in_lint_name() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2015" + + [lints.rust] + unexpected-cfgs = "allow" + "#, + ) + .file("src/lib.rs", "") + .build(); + + foo.cargo("check") + .with_stderr_data(str![[r#" +[WARNING] Cargo.toml: `lints.rust.unexpected-cfgs` is deprecated in favor of `lints.rust.unexpected_cfgs` and will not work in a future edition +[WARNING] `foo` (manifest) generated 1 warning +[CHECKING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + +#[cargo_test] +fn duplicate_lint_name_hyphen_and_underscore() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2015" + + [lints.rust] + unexpected_cfgs = "warn" + unexpected-cfgs = "allow" + "#, + ) + .file("src/lib.rs", "") + .build(); + + foo.cargo("check") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + duplicate lint `unexpected-cfgs` in `[lints.rust]`, conflicts with `unexpected_cfgs` + +"#]]) + .run(); +}