Skip to content

Commit d837917

Browse files
committed
Auto merge of #9799 - ehuss:fix-abnormal-error, r=alexcrichton
Show information about abnormal `fix` errors. During a recent crater run, we ran into a few circumstances where `cargo fix` failed unexpectedly, and we can't reproduce the errors locally. The sequence was: 1. Cargo ran `rustc` and collected the diagnostics to apply, and modified the files. 2. Cargo ran `rustc` again to verify the fixes. This step failed, but only emitted warnings. 3. Cargo ran `rustc` again to show the original diagnostics, and this exited normally with warnings. We don't know why the second step failed. This change makes it so that cargo will collect any non-diagnostic messages (like ICEs), and will also display the exit code if it is abnormal.
2 parents 8507683 + 5f274b0 commit d837917

File tree

3 files changed

+105
-4
lines changed

3 files changed

+105
-4
lines changed

src/cargo/ops/fix.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use std::process::{self, Command, ExitStatus};
4646
use std::str;
4747

4848
use anyhow::{bail, Context, Error};
49-
use cargo_util::{paths, ProcessBuilder};
49+
use cargo_util::{exit_status_to_string, is_simple_exit_code, paths, ProcessBuilder};
5050
use log::{debug, trace, warn};
5151
use rustfix::diagnostics::Diagnostic;
5252
use rustfix::{self, CodeFix};
@@ -374,7 +374,7 @@ pub fn fix_maybe_exec_rustc(config: &Config) -> CargoResult<bool> {
374374
paths::write(path, &file.original_code)?;
375375
}
376376
}
377-
log_failed_fix(&output.stderr)?;
377+
log_failed_fix(&output.stderr, output.status)?;
378378
}
379379
}
380380

@@ -662,7 +662,7 @@ fn exit_with(status: ExitStatus) -> ! {
662662
process::exit(status.code().unwrap_or(3));
663663
}
664664

665-
fn log_failed_fix(stderr: &[u8]) -> Result<(), Error> {
665+
fn log_failed_fix(stderr: &[u8], status: ExitStatus) -> Result<(), Error> {
666666
let stderr = str::from_utf8(stderr).context("failed to parse rustc stderr as utf-8")?;
667667

668668
let diagnostics = stderr
@@ -677,6 +677,13 @@ fn log_failed_fix(stderr: &[u8]) -> Result<(), Error> {
677677
files.insert(span.file_name);
678678
}
679679
}
680+
// Include any abnormal messages (like an ICE or whatever).
681+
errors.extend(
682+
stderr
683+
.lines()
684+
.filter(|x| !x.starts_with('{'))
685+
.map(|x| x.to_string()),
686+
);
680687
let mut krate = None;
681688
let mut prev_dash_dash_krate_name = false;
682689
for arg in env::args() {
@@ -692,10 +699,16 @@ fn log_failed_fix(stderr: &[u8]) -> Result<(), Error> {
692699
}
693700

694701
let files = files.into_iter().collect();
702+
let abnormal_exit = if status.code().map_or(false, is_simple_exit_code) {
703+
None
704+
} else {
705+
Some(exit_status_to_string(status))
706+
};
695707
Message::FixFailed {
696708
files,
697709
krate,
698710
errors,
711+
abnormal_exit,
699712
}
700713
.post()?;
701714

src/cargo/util/diagnostic_server.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub enum Message {
4848
files: Vec<String>,
4949
krate: Option<String>,
5050
errors: Vec<String>,
51+
abnormal_exit: Option<String>,
5152
},
5253
ReplaceFailed {
5354
file: String,
@@ -135,6 +136,7 @@ impl<'a> DiagnosticPrinter<'a> {
135136
files,
136137
krate,
137138
errors,
139+
abnormal_exit,
138140
} => {
139141
if let Some(ref krate) = *krate {
140142
self.config.shell().warn(&format!(
@@ -171,6 +173,13 @@ impl<'a> DiagnosticPrinter<'a> {
171173
}
172174
}
173175
}
176+
if let Some(exit) = abnormal_exit {
177+
writeln!(
178+
self.config.shell().err(),
179+
"rustc exited abnormally: {}",
180+
exit
181+
)?;
182+
}
174183
writeln!(
175184
self.config.shell().err(),
176185
"Original diagnostics will follow.\n"

tests/testsuite/fix.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use cargo::core::Edition;
44
use cargo_test_support::compare::assert_match_exact;
55
use cargo_test_support::git;
6-
use cargo_test_support::paths::CargoPathExt;
6+
use cargo_test_support::paths::{self, CargoPathExt};
77
use cargo_test_support::registry::{Dependency, Package};
88
use cargo_test_support::tools;
99
use cargo_test_support::{basic_manifest, is_nightly, project};
@@ -1597,3 +1597,82 @@ fn fix_shared_cross_workspace() {
15971597
&p.read_file("foo/src/shared.rs"),
15981598
);
15991599
}
1600+
1601+
#[cargo_test]
1602+
fn abnormal_exit() {
1603+
// rustc fails unexpectedly after applying fixes, should show some error information.
1604+
//
1605+
// This works with a proc-macro that runs three times:
1606+
// - First run (collect diagnostics pass): writes a file, exits normally.
1607+
// - Second run (verify diagnostics work): it detects the presence of the
1608+
// file, removes the file, and aborts the process.
1609+
// - Third run (collecting messages to display): file not found, exits normally.
1610+
let p = project()
1611+
.file(
1612+
"Cargo.toml",
1613+
r#"
1614+
[package]
1615+
name = "foo"
1616+
version = "0.1.0"
1617+
1618+
[dependencies]
1619+
pm = {path="pm"}
1620+
"#,
1621+
)
1622+
.file(
1623+
"src/lib.rs",
1624+
r#"
1625+
pub fn f() {
1626+
let mut x = 1;
1627+
pm::crashme!();
1628+
}
1629+
"#,
1630+
)
1631+
.file(
1632+
"pm/Cargo.toml",
1633+
r#"
1634+
[package]
1635+
name = "pm"
1636+
version = "0.1.0"
1637+
edition = "2018"
1638+
1639+
[lib]
1640+
proc-macro = true
1641+
"#,
1642+
)
1643+
.file(
1644+
"pm/src/lib.rs",
1645+
r#"
1646+
use proc_macro::TokenStream;
1647+
#[proc_macro]
1648+
pub fn crashme(_input: TokenStream) -> TokenStream {
1649+
// Use a file to succeed on the first pass, and fail on the second.
1650+
let p = std::env::var_os("ONCE_PATH").unwrap();
1651+
let check_path = std::path::Path::new(&p);
1652+
if check_path.exists() {
1653+
eprintln!("I'm not a diagnostic.");
1654+
std::fs::remove_file(check_path).unwrap();
1655+
std::process::abort();
1656+
} else {
1657+
std::fs::write(check_path, "").unwrap();
1658+
"".parse().unwrap()
1659+
}
1660+
}
1661+
"#,
1662+
)
1663+
.build();
1664+
1665+
p.cargo("fix --lib --allow-no-vcs")
1666+
.env(
1667+
"ONCE_PATH",
1668+
paths::root().join("proc-macro-run-once").to_str().unwrap(),
1669+
)
1670+
.with_stderr_contains(
1671+
"[WARNING] failed to automatically apply fixes suggested by rustc to crate `foo`",
1672+
)
1673+
.with_stderr_contains("I'm not a diagnostic.")
1674+
// "signal: 6, SIGABRT: process abort signal" on some platforms
1675+
.with_stderr_contains("rustc exited abnormally: [..]")
1676+
.with_stderr_contains("Original diagnostics will follow.")
1677+
.run();
1678+
}

0 commit comments

Comments
 (0)