-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add option to error when warnings are emitted, or ignore warnings #12875
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
use anyhow::{anyhow, Context as _}; | ||
use cargo::core::shell::Shell; | ||
use cargo::core::{features, CliUnstable}; | ||
use cargo::util::config::WarningHandling; | ||
use cargo::{self, drop_print, drop_println, CargoResult, CliResult, Config}; | ||
use clap::{builder::UnknownArgumentValueParser, Arg, ArgMatches}; | ||
use itertools::Itertools; | ||
|
@@ -181,7 +182,9 @@ Run with 'cargo -Z [FLAG] [COMMAND]'", | |
config_configure(config, &expanded_args, subcommand_args, global_args, &exec)?; | ||
super::init_git(config); | ||
|
||
exec.exec(config, subcommand_args) | ||
let result = exec.exec(config, subcommand_args); | ||
config.shell().error_for_warnings()?; | ||
result | ||
} | ||
|
||
pub fn get_version_string(is_verbose: bool) -> String { | ||
|
@@ -394,6 +397,13 @@ fn config_configure( | |
let frozen = args.flag("frozen") || global_args.frozen; | ||
let locked = args.flag("locked") || global_args.locked; | ||
let offline = args.flag("offline") || global_args.offline; | ||
let warnings = match args.get_one::<String>("warnings").map(String::as_str) { | ||
Some("ignore") => Some(WarningHandling::Ignore), | ||
Some("warn") => Some(WarningHandling::Warn), | ||
Some("error") => Some(WarningHandling::Error), | ||
None => None, | ||
_ => unreachable!(), | ||
}; | ||
let mut unstable_flags = global_args.unstable_flags; | ||
if let Some(values) = args.get_many::<String>("unstable-features") { | ||
unstable_flags.extend(values.cloned()); | ||
|
@@ -405,6 +415,7 @@ fn config_configure( | |
config.configure( | ||
verbose, | ||
quiet, | ||
warnings, | ||
color, | ||
frozen, | ||
locked, | ||
|
@@ -595,6 +606,11 @@ See '<cyan,bold>cargo help</> <cyan><<command>></>' for more information on a sp | |
.value_name("WHEN") | ||
.global(true), | ||
) | ||
.arg( | ||
opt("warnings", "Warning behavior") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We likely should if we move forward with this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I lean towards
|
||
.value_parser(["error", "warn", "ignore"]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can use |
||
.global(true), | ||
) | ||
.arg( | ||
Arg::new("directory") | ||
.help("Change to DIRECTORY before doing anything (nightly-only)") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -140,6 +140,7 @@ use crate::core::compiler::future_incompat::{ | |
}; | ||
use crate::core::resolver::ResolveBehavior; | ||
use crate::core::{PackageId, Shell, TargetKind}; | ||
use crate::util::config::WarningHandling; | ||
use crate::util::diagnostic_server::{self, DiagnosticPrinter}; | ||
use crate::util::errors::AlreadyPrintedError; | ||
use crate::util::machine_message::{self, Message as _}; | ||
|
@@ -314,8 +315,10 @@ impl<'cfg> DiagDedupe<'cfg> { | |
return Ok(false); | ||
} | ||
let mut shell = self.config.shell(); | ||
shell.print_ansi_stderr(diag.as_bytes())?; | ||
shell.err().write_all(b"\n")?; | ||
if shell.warnings() != WarningHandling::Ignore { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels weird that |
||
shell.print_ansi_stderr(diag.as_bytes())?; | ||
shell.err().write_all(b"\n")?; | ||
Comment on lines
+318
to
+320
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I missed it but how are we turning compiler warnings into errors? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're not turning the warnings into errors. They're still printed as warnings. After all the warnings are emitted, a single error is emitted if any warnings were present. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put another way, how are we tracking compiler warnings to fail the process. |
||
} | ||
Ok(true) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ use std::io::IsTerminal; | |
use anstream::AutoStream; | ||
use anstyle::Style; | ||
|
||
use crate::util::config::WarningHandling; | ||
use crate::util::errors::CargoResult; | ||
use crate::util::style::*; | ||
|
||
|
@@ -57,6 +58,10 @@ pub struct Shell { | |
/// Flag that indicates the current line needs to be cleared before | ||
/// printing. Used when a progress bar is currently displayed. | ||
needs_clear: bool, | ||
/// How warning should be handled. | ||
warnings: WarningHandling, | ||
/// The number of warnings so far. | ||
warning_count: usize, | ||
} | ||
|
||
impl fmt::Debug for Shell { | ||
|
@@ -115,6 +120,8 @@ impl Shell { | |
}, | ||
verbosity: Verbosity::Verbose, | ||
needs_clear: false, | ||
warnings: WarningHandling::Warn, | ||
warning_count: 0, | ||
} | ||
} | ||
|
||
|
@@ -124,6 +131,8 @@ impl Shell { | |
output: ShellOut::Write(AutoStream::never(out)), // strip all formatting on write | ||
verbosity: Verbosity::Verbose, | ||
needs_clear: false, | ||
warnings: WarningHandling::Warn, | ||
warning_count: 0, | ||
} | ||
} | ||
|
||
|
@@ -263,10 +272,13 @@ impl Shell { | |
|
||
/// Prints an amber 'warning' message. | ||
pub fn warn<T: fmt::Display>(&mut self, message: T) -> CargoResult<()> { | ||
match self.verbosity { | ||
Verbosity::Quiet => Ok(()), | ||
_ => self.print(&"warning", Some(&message), &WARN, false), | ||
self.warning_count += 1; | ||
if matches!(self.verbosity, Verbosity::Quiet) | ||
|| matches!(self.warnings, WarningHandling::Ignore) | ||
{ | ||
return Ok(()); | ||
} | ||
self.print(&"warning", Some(&message), &WARN, false) | ||
Comment on lines
274
to
+281
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
/// Prints a cyan 'note' message. | ||
|
@@ -284,6 +296,31 @@ impl Shell { | |
self.verbosity | ||
} | ||
|
||
/// Updates how warnings are handled. | ||
pub fn set_warnings(&mut self, warnings: WarningHandling) { | ||
self.warnings = warnings; | ||
} | ||
|
||
/// Gets the warning handling behavior. | ||
pub fn warnings(&self) -> WarningHandling { | ||
self.warnings | ||
} | ||
|
||
/// Emit an error if any warnings have been seen. | ||
pub fn error_for_warnings(&self) -> CargoResult<()> { | ||
if self.warning_count > 0 && self.warnings == WarningHandling::Error { | ||
let quiet_note = if self.verbosity == Verbosity::Quiet { | ||
" (note that verbosity is set to quiet, which may hide warnings)" | ||
} else { | ||
"" | ||
}; | ||
anyhow::bail!( | ||
"warnings detected and warnings are disallowed by configuration{quiet_note}" | ||
); | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// Updates the color choice (always, never, or auto) from a string.. | ||
pub fn set_color_choice(&mut self, color: Option<&str>) -> CargoResult<()> { | ||
if let ShellOut::Stream { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1257,6 +1257,19 @@ Controls whether or not extra detailed messages are displayed by Cargo. | |
Specifying the `--quiet` flag will override and disable verbose output. | ||
Specifying the `--verbose` flag will override and force verbose output. | ||
|
||
#### `term.warnings` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We have Maybe we should have some kind of general category? |
||
* Type: string | ||
* Default: "warn" | ||
* Environment: `CARGO_TERM_WARNINGS` | ||
|
||
Overrides how warnings are handled, including warnings that come from `rustc`. | ||
|
||
The `--warnings=` CLI option overrides this configuration value. | ||
|
||
* `warn` (default): warnings are displayed and do not fail the operation. | ||
* `error`: if any warnings are encountered an error will be emitted at the of the operation. | ||
* `ignore`: warnings will be silently ignored. | ||
Comment on lines
+1269
to
+1271
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How come you did On one hand, the semantics are slightly different than the levels but on the other hand, they are familiar, predictable terms There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seemed clearer what the behavior would be. If we do Maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
#### `term.color` | ||
* Type: string | ||
* Default: "auto" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,6 +107,7 @@ impl ConfigBuilder { | |
0, | ||
false, | ||
None, | ||
None, | ||
false, | ||
false, | ||
false, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
//! Tests for overriding warning behavior using `--warnings=` on the CLI or `term.warnings` config option. | ||
|
||
use cargo_test_support::{project, Project}; | ||
|
||
const WARNING1: &'static str = "[WARNING] unused variable: `x`"; | ||
const WARNING2: &'static str = "[WARNING] unused config key `build.xyz` in `[..]`"; | ||
|
||
fn make_project(main_src: &str) -> Project { | ||
project() | ||
.file( | ||
"Cargo.toml", | ||
r#" | ||
[package] | ||
name = "foo" | ||
version = "0.0.1" | ||
authors = [] | ||
"#, | ||
) | ||
.file("src/main.rs", &format!("fn main() {{ {} }}", main_src)) | ||
.build() | ||
} | ||
|
||
#[cargo_test] | ||
fn rustc_warnings() { | ||
let p = make_project("let x = 3;"); | ||
p.cargo("check --warnings=warn") | ||
.with_stderr_contains(WARNING1) | ||
.run(); | ||
|
||
p.cargo("check --warnings=error") | ||
.with_stderr_contains(WARNING1) | ||
.with_stderr_contains( | ||
"[ERROR] warnings detected and warnings are disallowed by configuration", | ||
) | ||
.with_status(101) | ||
.run(); | ||
|
||
p.cargo("check --warnings=ignore") | ||
.with_stderr_does_not_contain(WARNING1) | ||
.run(); | ||
} | ||
|
||
#[cargo_test] | ||
fn config() { | ||
let p = make_project("let x = 3;"); | ||
p.cargo("check") | ||
.env("CARGO_TERM_WARNINGS", "error") | ||
.with_stderr_contains(WARNING1) | ||
.with_stderr_contains( | ||
"[ERROR] warnings detected and warnings are disallowed by configuration", | ||
) | ||
.with_status(101) | ||
.run(); | ||
|
||
// CLI has precedence over config | ||
p.cargo("check --warnings=ignore") | ||
.env("CARGO_TERM_WARNINGS", "error") | ||
.with_stderr_does_not_contain(WARNING1) | ||
.run(); | ||
|
||
p.cargo("check") | ||
.env("CARGO_TERM_WARNINGS", "ignore") | ||
.with_stderr_does_not_contain(WARNING1) | ||
.run(); | ||
} | ||
|
||
#[cargo_test] | ||
/// Warnings that come from cargo rather than rustc | ||
fn cargo_warnings() { | ||
let p = make_project(""); | ||
p.change_file(".cargo/config.toml", "[build]\nxyz = false"); | ||
p.cargo("check").with_stderr_contains(WARNING2).run(); | ||
|
||
p.cargo("check --warnings=error") | ||
.with_stderr_contains(WARNING2) | ||
.with_stderr_contains( | ||
"[ERROR] warnings detected and warnings are disallowed by configuration", | ||
) | ||
.with_status(101) | ||
.run(); | ||
|
||
p.cargo("check --warnings=ignore") | ||
.with_stderr_does_not_contain(WARNING2) | ||
.run(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not every command comes back to this, like
cargo run
does anexec_replace