Skip to content

Commit c3bad93

Browse files
committed
feat(warnings) add build.warnings option
1 parent 643a025 commit c3bad93

File tree

12 files changed

+346
-53
lines changed

12 files changed

+346
-53
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: 18 additions & 10 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
);
@@ -963,32 +970,32 @@ impl<'gctx> DrainState<'gctx> {
963970
}
964971

965972
fn emit_warnings(
966-
&mut self,
973+
&self,
967974
msg: Option<&str>,
968975
unit: &Unit,
969-
build_runner: &mut BuildRunner<'_, '_>,
976+
build_runner: &BuildRunner<'_, '_>,
970977
) -> CargoResult<()> {
971978
let outputs = build_runner.build_script_outputs.lock().unwrap();
972979
let Some(metadata) = build_runner.find_build_script_metadata(unit) else {
973980
return Ok(());
974981
};
975-
let bcx = &mut build_runner.bcx;
982+
let gctx = build_runner.bcx.gctx;
976983
if let Some(output) = outputs.get(metadata) {
977984
if !output.warnings.is_empty() {
978985
if let Some(msg) = msg {
979-
writeln!(bcx.gctx.shell().err(), "{}\n", msg)?;
986+
writeln!(gctx.shell().err(), "{}\n", msg)?;
980987
}
981988

982989
for warning in output.warnings.iter() {
983990
let warning_with_package =
984991
format!("{}@{}: {}", unit.pkg.name(), unit.pkg.version(), warning);
985992

986-
bcx.gctx.shell().warn(warning_with_package)?;
993+
gctx.shell().warn(warning_with_package)?;
987994
}
988995

989996
if msg.is_some() {
990997
// Output an empty line.
991-
writeln!(bcx.gctx.shell().err())?;
998+
writeln!(gctx.shell().err())?;
992999
}
9931000
}
9941001
}
@@ -1022,13 +1029,13 @@ impl<'gctx> DrainState<'gctx> {
10221029
gctx: &GlobalContext,
10231030
id: JobId,
10241031
rustc_workspace_wrapper: &Option<PathBuf>,
1025-
) {
1026-
let count = match self.warning_count.remove(&id) {
1032+
) -> usize {
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
10301037
Some(count) if count.total > 0 => count,
1031-
None | Some(_) => return,
1038+
None | Some(_) => return 0,
10321039
};
10331040
let unit = &self.active[&id];
10341041
let mut message = descriptive_pkg_name(&unit.pkg.name(), &unit.target, &unit.mode);
@@ -1089,6 +1096,7 @@ impl<'gctx> DrainState<'gctx> {
10891096
// Errors are ignored here because it is tricky to handle them
10901097
// correctly, and they aren't important.
10911098
let _ = gctx.shell().warn(message);
1099+
count.total
10921100
}
10931101

10941102
fn finish(

src/cargo/core/compiler/mod.rs

Lines changed: 8 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,16 @@ 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()?
207+
!= WarningHandling::Allow;
204208
let work = replay_output_cache(
205209
unit.pkg.package_id(),
206210
PathBuf::from(unit.pkg.manifest_path()),
207211
&unit.target,
208212
build_runner.files().message_cache_path(unit),
209213
build_runner.bcx.build_config.message_format,
210-
unit.show_warnings(bcx.gctx),
214+
show_diagnostics,
211215
);
212216
// Need to link targets on both the dirty and fresh.
213217
work.then(link_targets(build_runner, unit, true)?)
@@ -1665,10 +1669,12 @@ impl OutputOptions {
16651669
// Remove old cache, ignore ENOENT, which is the common case.
16661670
drop(fs::remove_file(&path));
16671671
let cache_cell = Some((path, LazyCell::new()));
1672+
let show_diagnostics =
1673+
build_runner.bcx.gctx.warning_handling().unwrap_or_default() != WarningHandling::Allow;
16681674
OutputOptions {
16691675
format: build_runner.bcx.build_config.message_format,
16701676
cache_cell,
1671-
show_diagnostics: true,
1677+
show_diagnostics,
16721678
warnings_seen: 0,
16731679
errors_seen: 0,
16741680
}

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/core/workspace.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,12 +1169,13 @@ impl<'gctx> Workspace<'gctx> {
11691169
}
11701170
}
11711171

1172-
pub fn emit_warnings(&self) -> CargoResult<()> {
1172+
pub fn emit_warnings(&self) -> CargoResult<usize> {
1173+
let mut warning_count = 0;
11731174
for (path, maybe_pkg) in &self.packages.packages {
11741175
let path = path.join("Cargo.toml");
11751176
if let MaybePackage::Package(pkg) = maybe_pkg {
11761177
if self.gctx.cli_unstable().cargo_lints {
1177-
self.emit_lints(pkg, &path)?
1178+
warning_count += self.emit_lints(pkg, &path)?;
11781179
}
11791180
}
11801181
let warnings = match maybe_pkg {
@@ -1199,11 +1200,11 @@ impl<'gctx> Workspace<'gctx> {
11991200
}
12001201
}
12011202
}
1202-
Ok(())
1203+
Ok(warning_count)
12031204
}
12041205

1205-
pub fn emit_lints(&self, pkg: &Package, path: &Path) -> CargoResult<()> {
1206-
let mut error_count = 0;
1206+
pub fn emit_lints(&self, pkg: &Package, path: &Path) -> CargoResult<usize> {
1207+
let mut counts = Default::default();
12071208
let toml_lints = pkg
12081209
.manifest()
12091210
.normalized_toml()
@@ -1235,16 +1236,17 @@ impl<'gctx> Workspace<'gctx> {
12351236
self.root_manifest(),
12361237
self.gctx,
12371238
)?;
1238-
check_im_a_teapot(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
1239-
check_implicit_features(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
1240-
unused_dependencies(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
1241-
if error_count > 0 {
1239+
check_im_a_teapot(pkg, &path, &cargo_lints, &mut counts, self.gctx)?;
1240+
check_implicit_features(pkg, &path, &cargo_lints, &mut counts, self.gctx)?;
1241+
unused_dependencies(pkg, &path, &cargo_lints, &mut counts, self.gctx)?;
1242+
if counts.error_count > 0 {
12421243
Err(crate::util::errors::AlreadyPrintedError::new(anyhow!(
1243-
"encountered {error_count} errors(s) while running lints"
1244+
"encountered {error_count} errors(s) while running lints",
1245+
error_count = counts.error_count
12441246
))
12451247
.into())
12461248
} else {
1247-
Ok(())
1249+
Ok(counts.warning_count)
12481250
}
12491251
}
12501252

src/cargo/ops/cargo_compile/mod.rs

Lines changed: 11 additions & 3 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

@@ -137,8 +137,16 @@ pub fn compile_with_exec<'a>(
137137
options: &CompileOptions,
138138
exec: &Arc<dyn Executor>,
139139
) -> CargoResult<Compilation<'a>> {
140-
ws.emit_warnings()?;
141-
compile_ws(ws, options, exec)
140+
let cargo_warning_count = ws.emit_warnings()?;
141+
let compilation = compile_ws(ws, options, exec)?;
142+
let total_warnings = cargo_warning_count + compilation.warning_count;
143+
144+
if ws.gctx().warning_handling()? == WarningHandling::Deny && total_warnings > 0 {
145+
anyhow::bail!(
146+
"warnings are denied by `build.warnings` configuration"
147+
)
148+
}
149+
Ok(compilation)
142150
}
143151

144152
/// 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`.

0 commit comments

Comments
 (0)