Combine rustc and cargo's diagnostic summaries

This works by introspecting rustc's error output, using the JSON format
to determine whether it's a warning or error, then skipping it
altogether if it's a summary of the diagnostics printed.

Before:

```
src/main.rs:1:10: warning: trait objects without an explicit `dyn` are deprecated
src/main.rs:1:1: error[E0601]: `main` function not found in crate `wrong`
src/main.rs:1:9: error[E0038]: the trait `Clone` cannot be made into an object
error: aborting due to 2 previous errors; 1 warning emitted
error: could not compile `wrong`

```

After:

```
$ cargo check --message-format short
src/main.rs:1:10: warning: trait objects without an explicit `dyn` are deprecated
src/main.rs:1:1: error[E0601]: `main` function not found in crate `wrong`
src/main.rs:1:9: error[E0038]: the trait `Clone` cannot be made into an object
error: could not compile `wrong` due to 2 previous errors; 1 warning emitted
```
This commit is contained in:
Joshua Nelson 2021-07-05 17:32:40 -04:00
parent 768b5658c2
commit 9c7cc545d7
7 changed files with 52 additions and 16 deletions

View file

@ -51,6 +51,7 @@ pub struct FutureBreakageItem {
#[derive(Serialize, Deserialize)]
pub struct Diagnostic {
pub rendered: String,
pub level: String,
}
/// The filename in the top-level `target` directory where we store

View file

@ -333,7 +333,20 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
},
)
.map_err(verbose_if_simple_exit_code)
.with_context(|| format!("could not compile `{}`", name))?;
.with_context(|| {
// adapted from rustc_errors/src/lib.rs
let warnings = match output_options.warnings_seen {
0 => String::new(),
1 => "; 1 warning emitted".to_string(),
count => format!("; {} warnings emitted", count),
};
let errors = match output_options.errors_seen {
0 => String::new(),
1 => " due to previous error".to_string(),
count => format!(" due to {} previous errors", count),
};
format!("could not compile `{}`{}{}", name, errors, warnings)
})?;
}
if rustc_dep_info_loc.exists() {
@ -1161,6 +1174,8 @@ struct OutputOptions {
/// Other types of messages are processed regardless
/// of the value of this flag
show_warnings: bool,
warnings_seen: usize,
errors_seen: usize,
}
impl OutputOptions {
@ -1177,6 +1192,8 @@ impl OutputOptions {
color,
cache_cell,
show_warnings: true,
warnings_seen: 0,
errors_seen: 0,
}
}
}
@ -1244,7 +1261,18 @@ fn on_stderr_line_inner(
}
};
let count_diagnostic = |level, options: &mut OutputOptions| {
if level == "warning" {
options.warnings_seen += 1;
} else if level == "error" {
options.errors_seen += 1;
}
};
if let Ok(report) = serde_json::from_str::<FutureIncompatReport>(compiler_message.get()) {
for item in &report.future_incompat_report {
count_diagnostic(&*item.diagnostic.level, options);
}
state.future_incompat_report(report.future_incompat_report);
return Ok(true);
}
@ -1265,8 +1293,14 @@ fn on_stderr_line_inner(
#[derive(serde::Deserialize)]
struct CompilerMessage {
rendered: String,
message: String,
level: String,
}
if let Ok(mut error) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
if error.level == "error" && error.message.starts_with("aborting due to") {
// Skip this line; we'll print our own summary at the end.
return Ok(true);
}
// state.stderr will add a newline
if error.rendered.ends_with('\n') {
error.rendered.pop();
@ -1281,6 +1315,7 @@ fn on_stderr_line_inner(
.expect("strip should never fail")
};
if options.show_warnings {
count_diagnostic(&error.level, options);
state.stderr(rendered)?;
}
return Ok(true);
@ -1368,6 +1403,14 @@ fn on_stderr_line_inner(
return Ok(true);
}
#[derive(serde::Deserialize)]
struct CompilerMessage {
level: String,
}
if let Ok(message) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
count_diagnostic(&message.level, options);
}
let msg = machine_message::FromCompiler {
package_id,
manifest_path,
@ -1399,6 +1442,8 @@ fn replay_output_cache(
color,
cache_cell: None,
show_warnings,
warnings_seen: 0,
errors_seen: 0,
};
Work::new(move |state| {
if !path.exists() {

View file

@ -588,12 +588,7 @@ fn cargo_compile_with_invalid_code() {
p.cargo("build")
.with_status(101)
.with_stderr_contains(
"\
[ERROR] could not compile `foo`
To learn more, run the command again with --verbose.\n",
)
.with_stderr_contains("[ERROR] could not compile `foo` due to previous error\n")
.run();
assert!(p.root().join("Cargo.lock").is_file());
}

View file

@ -1458,7 +1458,7 @@ fn build_deps_not_for_normal() {
.with_stderr_contains("[..]can't find crate for `aaaaa`[..]")
.with_stderr_contains(
"\
[ERROR] could not compile `foo`
[ERROR] could not compile `foo` due to previous error
Caused by:
process didn't exit successfully: [..]

View file

@ -803,8 +803,7 @@ fn short_message_format() {
.with_stderr_contains(
"\
src/lib.rs:1:27: error[E0308]: mismatched types
error: aborting due to previous error
error: could not compile `foo`
error: could not compile `foo` due to previous error
",
)
.run();

View file

@ -28,7 +28,7 @@ fn do_not_fix_broken_builds() {
p.cargo("fix --allow-no-vcs")
.env("__CARGO_FIX_YOLO", "1")
.with_status(101)
.with_stderr_contains("[ERROR] could not compile `foo`")
.with_stderr_contains("[ERROR] could not compile `foo` due to previous error")
.run();
assert!(p.read_file("src/lib.rs").contains("let mut x = 3;"));
}
@ -833,8 +833,6 @@ fn prepare_for_unstable() {
[ERROR] cannot migrate src/lib.rs to edition {next}
Edition {next} is unstable and not allowed in this release, consider trying the nightly release channel.
error: could not compile `foo`
To learn more, run the command again with --verbose.
", next=next))
.run();

View file

@ -742,9 +742,7 @@ fn compile_failure() {
found at `[..]target`
Caused by:
could not compile `foo`
To learn more, run the command again with --verbose.
could not compile `foo` due to previous error
",
)
.run();