Skip to content

Commit b7b9f87

Browse files
committed
feat: add build.warnings config option to control rustc warnings
1 parent 36cbb68 commit b7b9f87

File tree

9 files changed

+128
-25
lines changed

9 files changed

+128
-25
lines changed

src/cargo/core/compiler/compilation.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ pub struct Compilation<'gctx> {
122122
target_runners: HashMap<CompileKind, Option<(PathBuf, Vec<String>)>>,
123123
/// The linker to use for each host or target.
124124
target_linkers: HashMap<CompileKind, Option<PathBuf>>,
125+
126+
/// The total number of warnings emitted by the compilation.
127+
pub warning_count: usize,
125128
}
126129

127130
impl<'gctx> Compilation<'gctx> {
@@ -169,6 +172,7 @@ impl<'gctx> Compilation<'gctx> {
169172
.chain(Some(&CompileKind::Host))
170173
.map(|kind| Ok((*kind, target_linker(bcx, *kind)?)))
171174
.collect::<CargoResult<HashMap<_, _>>>()?,
175+
warning_count: 0,
172176
})
173177
}
174178

src/cargo/core/compiler/job_queue/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ use crate::core::compiler::future_incompat::{
140140
};
141141
use crate::core::resolver::ResolveBehavior;
142142
use crate::core::{PackageId, Shell, TargetKind};
143+
use crate::util::context::WarningHandling;
143144
use crate::util::diagnostic_server::{self, DiagnosticPrinter};
144145
use crate::util::errors::AlreadyPrintedError;
145146
use crate::util::machine_message::{self, Message as _};
@@ -601,6 +602,7 @@ impl<'gctx> DrainState<'gctx> {
601602
plan: &mut BuildPlan,
602603
event: Message,
603604
) -> Result<(), ErrorToHandle> {
605+
let warning_handling = build_runner.bcx.gctx.warning_handling()?;
604606
match event {
605607
Message::Run(id, cmd) => {
606608
build_runner
@@ -638,7 +640,9 @@ impl<'gctx> DrainState<'gctx> {
638640
}
639641
}
640642
Message::Warning { id, warning } => {
641-
build_runner.bcx.gctx.shell().warn(warning)?;
643+
if warning_handling != WarningHandling::Allow {
644+
build_runner.bcx.gctx.shell().warn(warning)?;
645+
}
642646
self.bump_warning_count(id, true, false);
643647
}
644648
Message::WarningCount {
@@ -826,6 +830,9 @@ impl<'gctx> DrainState<'gctx> {
826830
// `display_error` inside `handle_error`.
827831
Some(anyhow::Error::new(AlreadyPrintedError::new(error)))
828832
} else if self.queue.is_empty() && self.pending_queue.is_empty() {
833+
build_runner.compilation.warning_count +=
834+
self.warning_count.values().map(|c| c.total).sum::<usize>();
835+
829836
let profile_link = build_runner.bcx.gctx.shell().err_hyperlink(
830837
"https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles",
831838
);
@@ -1023,7 +1030,7 @@ impl<'gctx> DrainState<'gctx> {
10231030
id: JobId,
10241031
rustc_workspace_wrapper: &Option<PathBuf>,
10251032
) {
1026-
let count = match self.warning_count.remove(&id) {
1033+
let count = match self.warning_count.get(&id) {
10271034
// An error could add an entry for a `Unit`
10281035
// with 0 warnings but having fixable
10291036
// warnings be disallowed

src/cargo/core/compiler/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ pub use crate::core::compiler::unit::{Unit, UnitInterner};
9090
use crate::core::manifest::TargetSourcePath;
9191
use crate::core::profiles::{PanicStrategy, Profile, StripInner};
9292
use crate::core::{Feature, PackageId, Target, Verbosity};
93+
use crate::util::context::WarningHandling;
9394
use crate::util::errors::{CargoResult, VerboseError};
9495
use crate::util::interning::InternedString;
9596
use crate::util::machine_message::{self, Message};
@@ -201,13 +202,15 @@ fn compile<'gctx>(
201202
} else {
202203
// We always replay the output cache,
203204
// since it might contain future-incompat-report messages
205+
let show_diagnostics = unit.show_warnings(bcx.gctx)
206+
&& build_runner.bcx.gctx.warning_handling()? != WarningHandling::Allow;
204207
let work = replay_output_cache(
205208
unit.pkg.package_id(),
206209
PathBuf::from(unit.pkg.manifest_path()),
207210
&unit.target,
208211
build_runner.files().message_cache_path(unit),
209212
build_runner.bcx.build_config.message_format,
210-
unit.show_warnings(bcx.gctx),
213+
show_diagnostics,
211214
);
212215
// Need to link targets on both the dirty and fresh.
213216
work.then(link_targets(build_runner, unit, true)?)
@@ -1665,10 +1668,12 @@ impl OutputOptions {
16651668
// Remove old cache, ignore ENOENT, which is the common case.
16661669
drop(fs::remove_file(&path));
16671670
let cache_cell = Some((path, LazyCell::new()));
1671+
let show_diagnostics =
1672+
build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
16681673
OutputOptions {
16691674
format: build_runner.bcx.build_config.message_format,
16701675
cache_cell,
1671-
show_diagnostics: true,
1676+
show_diagnostics,
16721677
warnings_seen: 0,
16731678
errors_seen: 0,
16741679
}

src/cargo/core/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,7 @@ unstable_cli_options!(
790790
target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"),
791791
trim_paths: bool = ("Enable the `trim-paths` option in profiles"),
792792
unstable_options: bool = ("Allow the usage of unstable options"),
793+
warnings: bool = ("Allow use of the build.warnings config key"),
793794
);
794795

795796
const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
@@ -1293,6 +1294,7 @@ impl CliUnstable {
12931294
"script" => self.script = parse_empty(k, v)?,
12941295
"target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?,
12951296
"unstable-options" => self.unstable_options = parse_empty(k, v)?,
1297+
"warnings" => self.warnings = parse_empty(k, v)?,
12961298
_ => bail!("\
12971299
unknown `-Z` flag specified: {k}\n\n\
12981300
For available unstable features, see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html\n\

src/cargo/ops/cargo_compile/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use crate::core::{PackageId, PackageSet, SourceId, TargetKind, Workspace};
5252
use crate::drop_println;
5353
use crate::ops;
5454
use crate::ops::resolve::WorkspaceResolve;
55-
use crate::util::context::GlobalContext;
55+
use crate::util::context::{GlobalContext, WarningHandling};
5656
use crate::util::interning::InternedString;
5757
use crate::util::{CargoResult, StableHasher};
5858

@@ -138,7 +138,11 @@ pub fn compile_with_exec<'a>(
138138
exec: &Arc<dyn Executor>,
139139
) -> CargoResult<Compilation<'a>> {
140140
ws.emit_warnings()?;
141-
compile_ws(ws, options, exec)
141+
let compilation = compile_ws(ws, options, exec)?;
142+
if ws.gctx().warning_handling()? == WarningHandling::Deny && compilation.warning_count > 0 {
143+
anyhow::bail!("warnings are denied by `build.warnings` configuration")
144+
}
145+
Ok(compilation)
142146
}
143147

144148
/// Like [`compile_with_exec`] but without warnings from manifest parsing.

src/cargo/util/context/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,6 +2004,15 @@ impl GlobalContext {
20042004
})?;
20052005
Ok(deferred.borrow_mut())
20062006
}
2007+
2008+
/// Get the global [`WarningHandling`] configuration.
2009+
pub fn warning_handling(&self) -> CargoResult<WarningHandling> {
2010+
if self.unstable_flags.warnings {
2011+
Ok(self.build_config()?.warnings.unwrap_or_default())
2012+
} else {
2013+
Ok(Default::default())
2014+
}
2015+
}
20072016
}
20082017

20092018
/// Internal error for serde errors.
@@ -2615,6 +2624,20 @@ pub struct CargoBuildConfig {
26152624
// deprecated alias for artifact-dir
26162625
pub out_dir: Option<ConfigRelativePath>,
26172626
pub artifact_dir: Option<ConfigRelativePath>,
2627+
pub warnings: Option<WarningHandling>,
2628+
}
2629+
2630+
/// Whether warnings should warn, be allowed, or cause an error.
2631+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Default)]
2632+
#[serde(rename_all = "kebab-case")]
2633+
pub enum WarningHandling {
2634+
#[default]
2635+
/// Output warnings.
2636+
Warn,
2637+
/// Allow warnings (do not output them).
2638+
Allow,
2639+
/// Error if warnings are emitted.
2640+
Deny,
26182641
}
26192642

26202643
/// Configuration for `build.target`.

src/doc/src/reference/unstable.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ Each new feature described below should explain how to use it.
118118
* [lockfile-path](#lockfile-path) --- Allows to specify a path to lockfile other than the default path `<workspace_root>/Cargo.lock`.
119119
* [package-workspace](#package-workspace) --- Allows for packaging and publishing multiple crates in a workspace.
120120
* [native-completions](#native-completions) --- Move cargo shell completions to native completions.
121+
* [warnings](#warnings) --- controls warning behavior; options for allowing or denying warnings.
121122

122123
## allow-features
123124

@@ -1957,3 +1958,22 @@ default behavior.
19571958

19581959
See the [build script documentation](build-scripts.md#rustc-check-cfg) for information
19591960
about specifying custom cfgs.
1961+
1962+
## warnings
1963+
1964+
The `-Z warnings` feature enables the `build.warnings` configuration option to control how
1965+
Cargo handles warnings. If the `-Z warnings` unstable flag is not enabled, then
1966+
the `build.warnings` config will be ignored.
1967+
1968+
### `build.warnings`
1969+
* Type: string
1970+
* Default: `warn`
1971+
* Environment: `CARGO_BUILD_WARNINGS`
1972+
1973+
Controls how Cargo handles warnings. Allowed values are:
1974+
* `warn`: warnings are emitted as warnings (default).
1975+
* `allow`: warnings are hidden.
1976+
* `deny`: if warnings are emitted, an error will be raised at the end of the operation and the process will exit with a failure exit code.
1977+
1978+
This setting currently only applies to rustc warnings. It may apply to additional warnings (such as Cargo lints or Cargo warnings)
1979+
in the future.

tests/testsuite/cargo/z_help/stdout.term.svg

Lines changed: 8 additions & 6 deletions
Loading

tests/testsuite/warning_override.rs

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,13 @@ use snapbox::data::Inline;
88
const ALLOW_CLEAN: LazyLock<Inline> = LazyLock::new(|| {
99
str![[r#"
1010
[CHECKING] foo v0.0.1 ([ROOT]/foo)
11-
[WARNING] unused variable: `x`
12-
...
13-
[WARNING] `foo` (bin "foo") generated 1 warning
1411
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
1512
1613
"#]]
1714
});
1815

1916
const ALLOW_CACHED: LazyLock<Inline> = LazyLock::new(|| {
2017
str![[r#"
21-
[WARNING] unused variable: `x`
22-
...
23-
[WARNING] `foo` (bin "foo") generated 1 warning
2418
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
2519
2620
"#]]
@@ -44,6 +38,7 @@ const DENY: LazyLock<Inline> = LazyLock::new(|| {
4438
...
4539
[WARNING] `foo` (bin "foo") generated 1 warning
4640
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
41+
[ERROR] warnings are denied by `build.warnings` configuration
4742
4843
"#]]
4944
});
@@ -68,33 +63,74 @@ fn make_project(main_src: &str) -> Project {
6863
#[cargo_test]
6964
fn rustc_caching_allow_first() {
7065
let p = make_project("let x = 3;");
71-
p.cargo("check").with_stderr_data(ALLOW_CLEAN.clone()).run();
66+
p.cargo("check")
67+
.masquerade_as_nightly_cargo(&["warnings"])
68+
.arg("-Zwarnings")
69+
.arg("--config")
70+
.arg("build.warnings='allow'")
71+
.with_stderr_data(ALLOW_CLEAN.clone())
72+
.run();
7273

73-
p.cargo("check").with_stderr_data(DENY.clone()).run();
74+
p.cargo("check")
75+
.masquerade_as_nightly_cargo(&["warnings"])
76+
.arg("-Zwarnings")
77+
.arg("--config")
78+
.arg("build.warnings='deny'")
79+
.with_stderr_data(DENY.clone())
80+
.with_status(101)
81+
.run();
7482
}
7583

7684
#[cargo_test]
7785
fn rustc_caching_deny_first() {
7886
let p = make_project("let x = 3;");
79-
p.cargo("check").with_stderr_data(DENY.clone()).run();
87+
p.cargo("check")
88+
.masquerade_as_nightly_cargo(&["warnings"])
89+
.arg("-Zwarnings")
90+
.arg("--config")
91+
.arg("build.warnings='deny'")
92+
.with_stderr_data(DENY.clone())
93+
.with_status(101)
94+
.run();
8095

8196
p.cargo("check")
97+
.masquerade_as_nightly_cargo(&["warnings"])
98+
.arg("-Zwarnings")
99+
.arg("--config")
100+
.arg("build.warnings='allow'")
82101
.with_stderr_data(ALLOW_CACHED.clone())
83102
.run();
84103
}
85104

86105
#[cargo_test]
87106
fn config() {
88107
let p = make_project("let x = 3;");
89-
p.cargo("check").with_stderr_data(DENY.clone()).run();
108+
p.cargo("check")
109+
.masquerade_as_nightly_cargo(&["warnings"])
110+
.arg("-Zwarnings")
111+
.env("CARGO_BUILD_WARNINGS", "deny")
112+
.with_stderr_data(DENY.clone())
113+
.with_status(101)
114+
.run();
90115

91-
// CLI has precedence over env.
92-
p.cargo("check").with_stderr_data(WARN.clone()).run();
116+
// CLI has precedence over env
117+
p.cargo("check")
118+
.masquerade_as_nightly_cargo(&["warnings"])
119+
.arg("-Zwarnings")
120+
.arg("--config")
121+
.arg("build.warnings='warn'")
122+
.env("CARGO_BUILD_WARNINGS", "deny")
123+
.with_stderr_data(WARN.clone())
124+
.run();
93125
}
94126

95127
#[cargo_test]
96128
fn requires_nightly() {
97129
// build.warnings has no effect without -Zwarnings.
98130
let p = make_project("let x = 3;");
99-
p.cargo("check").with_stderr_data(WARN.clone()).run();
131+
p.cargo("check")
132+
.arg("--config")
133+
.arg("build.warnings='deny'")
134+
.with_stderr_data(WARN.clone())
135+
.run();
100136
}

0 commit comments

Comments
 (0)