From 88a8dab47feb87cafa524654bbc08f9938a72e80 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Sat, 30 May 2026 06:45:53 +0000 Subject: [PATCH 1/4] test(toml): add baseline tests for hyphenated and duplicate lint names --- tests/testsuite/lints_table.rs | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/testsuite/lints_table.rs b/tests/testsuite/lints_table.rs index 05edfec1342..def18ddfa9f 100644 --- a/tests/testsuite/lints_table.rs +++ b/tests/testsuite/lints_table.rs @@ -843,3 +843,58 @@ 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 = "warn" + "#, + ) + .file("src/lib.rs", "") + .build(); + + foo.cargo("check") + .with_stderr_data(str![[r#" +[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_stderr_data(str![[r#" +[CHECKING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} From 38527bb361d74545cd111499ddcc2cf7bc182c21 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Sun, 31 May 2026 05:25:18 +0000 Subject: [PATCH 2/4] fix(toml): warn on hyphen lint names --- src/cargo/util/toml/mod.rs | 8 ++++++++ tests/testsuite/lints/mod.rs | 3 ++- tests/testsuite/lints/unknown_lints.rs | 11 ++++++++--- tests/testsuite/lints_table.rs | 14 ++++++++++++-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 0799ef9d3f1..edebb44dac3 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -2691,6 +2691,14 @@ supported tools: {}", warn_for_cargo_lint_feature(gctx, warnings); } 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((prefix, suffix)) = name.split_once("::") { if tool == prefix { anyhow::bail!( 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 def18ddfa9f..a484600f0fc 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 @@ -856,7 +862,7 @@ fn hyphen_in_lint_name() { edition = "2015" [lints.rust] - unexpected-cfgs = "warn" + unexpected-cfgs = "allow" "#, ) .file("src/lib.rs", "") @@ -864,6 +870,8 @@ fn hyphen_in_lint_name() { 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 @@ -892,6 +900,8 @@ fn duplicate_lint_name_hyphen_and_underscore() { 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 From 4bb4f1b5950fb976396e5624b501423f7f07e528 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Sun, 31 May 2026 05:49:25 +0000 Subject: [PATCH 3/4] fix(toml): error on duplicate normalized lint names --- src/cargo/util/toml/mod.rs | 8 ++++++++ tests/testsuite/lints_table.rs | 9 +++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index edebb44dac3..38de40eb5ea 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -2690,6 +2690,7 @@ 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('-') { @@ -2699,6 +2700,13 @@ supported tools: {}", 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!( diff --git a/tests/testsuite/lints_table.rs b/tests/testsuite/lints_table.rs index a484600f0fc..a578116bbe8 100644 --- a/tests/testsuite/lints_table.rs +++ b/tests/testsuite/lints_table.rs @@ -899,11 +899,12 @@ fn duplicate_lint_name_hyphen_and_underscore() { .build(); foo.cargo("check") + .with_status(101) .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 +[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + duplicate lint `unexpected-cfgs` in `[lints.rust]`, conflicts with `unexpected_cfgs` "#]]) .run(); From 0f4460608bdd7f8bb34e7732414a96c123efd334 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Sat, 30 May 2026 07:41:18 +0000 Subject: [PATCH 4/4] refactor(toml): normalize lint names before passing to rustc flags --- src/cargo/util/toml/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 38de40eb5ea..796a1f93be6 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -2788,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(),