From 07662c90a448b14de5c3a17cae8d447d0c19414c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 5 Nov 2025 16:07:53 -0600 Subject: [PATCH 1/3] refactor(compile): Name boolean parameters --- src/cargo/core/compiler/job_queue/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/compiler/job_queue/mod.rs b/src/cargo/core/compiler/job_queue/mod.rs index de9332c91ab..918acee39b0 100644 --- a/src/cargo/core/compiler/job_queue/mod.rs +++ b/src/cargo/core/compiler/job_queue/mod.rs @@ -632,7 +632,9 @@ impl<'gctx> DrainState<'gctx> { if warning_handling != WarningHandling::Allow { build_runner.bcx.gctx.shell().warn(warning)?; } - self.bump_warning_count(id, true, false); + let emitted = true; + let fixable = false; + self.bump_warning_count(id, emitted, fixable); } Message::WarningCount { id, From dda6ea6caaafc0926a734bd94f74e9a0eb1278ee Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 5 Nov 2025 16:38:59 -0600 Subject: [PATCH 2/3] test(compile): Verify hard warning behavior --- tests/testsuite/warning_override.rs | 70 +++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/testsuite/warning_override.rs b/tests/testsuite/warning_override.rs index b83d9f92600..2f6b59ef881 100644 --- a/tests/testsuite/warning_override.rs +++ b/tests/testsuite/warning_override.rs @@ -221,3 +221,73 @@ Caused by: .with_status(101) .run(); } + +#[cargo_test] +fn hard_warning_deny() { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + "# + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("rustc") + .masquerade_as_nightly_cargo(&["warnings"]) + .arg("-Zwarnings") + .arg("--config") + .arg("build.warnings='deny'") + .arg("--") + .arg("-ox.rs") + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[WARNING] [..] + +[WARNING] [..] + +[WARNING] `foo` (bin "foo") generated 2 warnings +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[ERROR] warnings are denied by `build.warnings` configuration + +"#]]) + .with_status(101) + .run(); +} + +#[cargo_test] +fn hard_warning_allow() { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + edition = "2021" + "# + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + p.cargo("rustc") + .masquerade_as_nightly_cargo(&["warnings"]) + .arg("-Zwarnings") + .arg("--config") + .arg("build.warnings='allow'") + .arg("--") + .arg("-ox.rs") + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .with_status(0) + .run(); +} From 6fc976194cccdf7256db5bad9b7a67a4c2b9abdc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 5 Nov 2025 16:17:42 -0600 Subject: [PATCH 3/3] fix(compile): build.warnings=deny shouldn't block hard warnings Motivation for this: rustc is trying to adopt this feature but hard warnings are getting in the way, see https://github.com/rust-lang/rust/pull/148332 Reasons to do this: - Less flexibility in how hard warnings are resolved - Matches Cargo not erroring for Cargo hard warnings Reasons not do this: - A user may see a hard warning from rustc and get confused - Cargo's hard warnings are not blocking in part because we would need to audit them to see if they should actually be hard warnings We do have some flexibility on warnings evolving over time, so this is likely a two-way door (on an unstable feature). See also the discussion at https://github.com/rust-lang/cargo/issues/14802#issuecomment-3487140567 --- src/cargo/core/compiler/compilation.rs | 6 +++--- .../core/compiler/job_queue/job_state.rs | 10 +++++++++- src/cargo/core/compiler/job_queue/mod.rs | 20 ++++++++++++++----- src/cargo/core/compiler/mod.rs | 4 +++- src/cargo/ops/cargo_compile/mod.rs | 3 ++- tests/testsuite/warning_override.rs | 2 -- 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 68b59ba0cac..96c01a245db 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -123,8 +123,8 @@ pub struct Compilation<'gctx> { /// The linker to use for each host or target. target_linkers: HashMap>, - /// The total number of warnings emitted by the compilation. - pub warning_count: usize, + /// The total number of lint warnings emitted by the compilation. + pub lint_warning_count: usize, } impl<'gctx> Compilation<'gctx> { @@ -162,7 +162,7 @@ impl<'gctx> Compilation<'gctx> { .chain(Some(&CompileKind::Host)) .map(|kind| Ok((*kind, target_linker(bcx, *kind)?))) .collect::>>()?, - warning_count: 0, + lint_warning_count: 0, }) } diff --git a/src/cargo/core/compiler/job_queue/job_state.rs b/src/cargo/core/compiler/job_queue/job_state.rs index a2febe8f359..66ccff0bd3f 100644 --- a/src/cargo/core/compiler/job_queue/job_state.rs +++ b/src/cargo/core/compiler/job_queue/job_state.rs @@ -93,12 +93,19 @@ impl<'a, 'gctx> JobState<'a, 'gctx> { } /// See [`Message::Diagnostic`] and [`Message::WarningCount`]. - pub fn emit_diag(&self, level: &str, diag: String, fixable: bool) -> CargoResult<()> { + pub fn emit_diag( + &self, + level: &str, + diag: String, + lint: bool, + fixable: bool, + ) -> CargoResult<()> { if let Some(dedupe) = self.output { let emitted = dedupe.emit_diag(&diag)?; if level == "warning" { self.messages.push(Message::WarningCount { id: self.id, + lint, emitted, fixable, }); @@ -108,6 +115,7 @@ impl<'a, 'gctx> JobState<'a, 'gctx> { id: self.id, level: level.to_string(), diag, + lint, fixable, }); } diff --git a/src/cargo/core/compiler/job_queue/mod.rs b/src/cargo/core/compiler/job_queue/mod.rs index 918acee39b0..76efa68ddd4 100644 --- a/src/cargo/core/compiler/job_queue/mod.rs +++ b/src/cargo/core/compiler/job_queue/mod.rs @@ -207,6 +207,8 @@ struct DrainState<'gctx> { pub struct WarningCount { /// total number of warnings pub total: usize, + /// number of lint warnings + pub lints: usize, /// number of warnings that were suppressed because they /// were duplicates of a previous warning pub duplicates: usize, @@ -350,12 +352,14 @@ enum Message { id: JobId, level: String, diag: String, + lint: bool, fixable: bool, }, // This handles duplicate output that is suppressed, for showing // only a count of duplicate messages instead WarningCount { id: JobId, + lint: bool, emitted: bool, fixable: bool, }, @@ -616,11 +620,12 @@ impl<'gctx> DrainState<'gctx> { id, level, diag, + lint, fixable, } => { let emitted = self.diag_dedupe.emit_diag(&diag)?; if level == "warning" { - self.bump_warning_count(id, emitted, fixable); + self.bump_warning_count(id, lint, emitted, fixable); } if level == "error" { let cnts = self.warning_count.entry(id).or_default(); @@ -632,16 +637,18 @@ impl<'gctx> DrainState<'gctx> { if warning_handling != WarningHandling::Allow { build_runner.bcx.gctx.shell().warn(warning)?; } + let lint = false; let emitted = true; let fixable = false; - self.bump_warning_count(id, emitted, fixable); + self.bump_warning_count(id, lint, emitted, fixable); } Message::WarningCount { id, + lint, emitted, fixable, } => { - self.bump_warning_count(id, emitted, fixable); + self.bump_warning_count(id, lint, emitted, fixable); } Message::FixDiagnostic(msg) => { self.print.print(&msg)?; @@ -1004,9 +1011,12 @@ impl<'gctx> DrainState<'gctx> { Ok(()) } - fn bump_warning_count(&mut self, id: JobId, emitted: bool, fixable: bool) { + fn bump_warning_count(&mut self, id: JobId, lint: bool, emitted: bool, fixable: bool) { let cnts = self.warning_count.entry(id).or_default(); cnts.total += 1; + if lint { + cnts.lints += 1; + } if !emitted { cnts.duplicates += 1; // Don't add to fixable if it's already been emitted @@ -1039,7 +1049,7 @@ impl<'gctx> DrainState<'gctx> { Some(count) if count.total > 0 => count, None | Some(_) => return, }; - runner.compilation.warning_count += count.total; + runner.compilation.lint_warning_count += count.lints; let unit = &self.active[&id]; let mut message = descriptive_pkg_name(&unit.pkg.name(), &unit.target, &unit.mode); message.push_str(" generated "); diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 2b74735c492..908ad6aeb5c 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -2162,12 +2162,14 @@ fn on_stderr_line_inner( count_diagnostic(&msg.level, options); if msg .code + .as_ref() .is_some_and(|c| c.code == "exported_private_dependencies") && options.format != MessageFormat::Short { add_pub_in_priv_diagnostic(&mut rendered); } - state.emit_diag(&msg.level, rendered, machine_applicable)?; + let lint = msg.code.is_some(); + state.emit_diag(&msg.level, rendered, lint, machine_applicable)?; } return Ok(true); } diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index e4e4b830b3e..f95e907e6a6 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -143,7 +143,8 @@ pub fn compile_with_exec<'a>( ) -> CargoResult> { ws.emit_warnings()?; let compilation = compile_ws(ws, options, exec)?; - if ws.gctx().warning_handling()? == WarningHandling::Deny && compilation.warning_count > 0 { + if ws.gctx().warning_handling()? == WarningHandling::Deny && compilation.lint_warning_count > 0 + { anyhow::bail!("warnings are denied by `build.warnings` configuration") } Ok(compilation) diff --git a/tests/testsuite/warning_override.rs b/tests/testsuite/warning_override.rs index 2f6b59ef881..127c3377855 100644 --- a/tests/testsuite/warning_override.rs +++ b/tests/testsuite/warning_override.rs @@ -253,10 +253,8 @@ fn hard_warning_deny() { [WARNING] `foo` (bin "foo") generated 2 warnings [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s -[ERROR] warnings are denied by `build.warnings` configuration "#]]) - .with_status(101) .run(); }