Skip to content

Commit fffb05d

Browse files
committed
Display errors when cargo fix fails.
It can be difficult to figure out what's wrong when a user reports that `cargo fix` fails. There's often a large list of warnings, and it can be hard to figure out which one caused a compile error.
1 parent 79f962f commit fffb05d

File tree

4 files changed

+63
-13
lines changed

4 files changed

+63
-13
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ log = "0.4.6"
4545
libgit2-sys = "0.7.9"
4646
num_cpus = "1.0"
4747
opener = "0.3.0"
48-
rustfix = "0.4.2"
48+
rustfix = "0.4.3"
4949
same-file = "1"
5050
semver = { version = "0.9.0", features = ["serde"] }
5151
serde = { version = "1.0.82", features = ['derive'] }
@@ -108,3 +108,6 @@ doc = false
108108
deny-warnings = []
109109
vendored-openssl = ['openssl/vendored']
110110
pretty-env-logger = ['pretty_env_logger']
111+
112+
[patch.crates-io]
113+
rustfix = {git="https://github.com/ehuss/rustfix", branch="pub-rendered"}

src/cargo/ops/fix.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,9 @@ fn log_failed_fix(stderr: &[u8]) -> Result<(), Error> {
496496
.filter(|x| !x.is_empty())
497497
.filter_map(|line| serde_json::from_str::<Diagnostic>(line).ok());
498498
let mut files = BTreeSet::new();
499+
let mut errors = Vec::new();
499500
for diagnostic in diagnostics {
501+
errors.push(diagnostic.rendered.unwrap_or(diagnostic.message));
500502
for span in diagnostic.spans.into_iter() {
501503
files.insert(span.file_name);
502504
}
@@ -516,7 +518,12 @@ fn log_failed_fix(stderr: &[u8]) -> Result<(), Error> {
516518
}
517519

518520
let files = files.into_iter().collect();
519-
Message::FixFailed { files, krate }.post()?;
521+
Message::FixFailed {
522+
files,
523+
krate,
524+
errors,
525+
}
526+
.post()?;
520527

521528
Ok(())
522529
}

src/cargo/util/diagnostic_server.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub enum Message {
3636
FixFailed {
3737
files: Vec<String>,
3838
krate: Option<String>,
39+
errors: Vec<String>,
3940
},
4041
ReplaceFailed {
4142
file: String,
@@ -109,7 +110,11 @@ impl<'a> DiagnosticPrinter<'a> {
109110
write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
110111
Ok(())
111112
}
112-
Message::FixFailed { files, krate } => {
113+
Message::FixFailed {
114+
files,
115+
krate,
116+
errors,
117+
} => {
113118
if let Some(ref krate) = *krate {
114119
self.config.shell().warn(&format!(
115120
"failed to automatically apply fixes suggested by rustc \
@@ -133,6 +138,22 @@ impl<'a> DiagnosticPrinter<'a> {
133138
writeln!(self.config.shell().err())?;
134139
}
135140
write!(self.config.shell().err(), "{}", PLEASE_REPORT_THIS_BUG)?;
141+
if !errors.is_empty() {
142+
writeln!(
143+
self.config.shell().err(),
144+
"The following errors were reported:"
145+
)?;
146+
for error in errors {
147+
write!(self.config.shell().err(), "{}", error)?;
148+
if !error.ends_with('\n') {
149+
writeln!(self.config.shell().err())?;
150+
}
151+
}
152+
}
153+
writeln!(
154+
self.config.shell().err(),
155+
"Original diagnostics will follow.\n"
156+
)?;
136157
Ok(())
137158
}
138159
Message::EditionAlreadyEnabled { file, edition } => {

tests/testsuite/fix.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ fn fix_broken_if_requested() {
5454

5555
#[test]
5656
fn broken_fixes_backed_out() {
57+
// This works as follows:
58+
// - Create a `rustc` shim (the "foo" project) which will pretend that the
59+
// verification step fails.
60+
// - There is an empty build script so `foo` has `OUT_DIR` to track the steps.
61+
// - The first "check", `foo` creates a file in OUT_DIR, and it completes
62+
// successfully with a warning diagnostic to remove unused `mut`.
63+
// - rustfix removes the `mut`.
64+
// - The second "check" to verify the changes, `foo` swaps out the content
65+
// with something that fails to compile. It creates a second file so it
66+
// won't do anything in the third check.
67+
// - cargo fix discovers that the fix failed, and it backs out the changes.
68+
// - The third "check" is done to display the original diagnostics of the
69+
// original code.
5770
let p = project()
5871
.file(
5972
"foo/Cargo.toml",
@@ -74,19 +87,19 @@ fn broken_fixes_backed_out() {
7487
use std::process::{self, Command};
7588
7689
fn main() {
90+
// Ignore calls to things like --print=file-names and compiling build.rs.
7791
let is_lib_rs = env::args_os()
7892
.map(PathBuf::from)
7993
.any(|l| l == Path::new("src/lib.rs"));
8094
if is_lib_rs {
8195
let path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
82-
let path = path.join("foo");
83-
if path.exists() {
84-
fs::File::create("src/lib.rs")
85-
.unwrap()
86-
.write_all(b"not rust code")
87-
.unwrap();
96+
let first = path.join("first");
97+
let second = path.join("second");
98+
if first.exists() && !second.exists() {
99+
fs::write("src/lib.rs", b"not rust code").unwrap();
100+
fs::File::create(&second).unwrap();
88101
} else {
89-
fs::File::create(&path).unwrap();
102+
fs::File::create(&first).unwrap();
90103
}
91104
}
92105
@@ -127,8 +140,6 @@ fn broken_fixes_backed_out() {
127140
.cwd(p.root().join("bar"))
128141
.env("__CARGO_FIX_YOLO", "1")
129142
.env("RUSTC", p.root().join("foo/target/debug/foo"))
130-
.with_status(101)
131-
.with_stderr_contains("[..]not rust code[..]")
132143
.with_stderr_contains(
133144
"\
134145
warning: failed to automatically apply fixes suggested by rustc \
@@ -144,11 +155,19 @@ fn broken_fixes_backed_out() {
144155
a number of compiler warnings after this message which cargo\n\
145156
attempted to fix but failed. If you could open an issue at\n\
146157
https://github.com/rust-lang/cargo/issues\n\
147-
quoting the full output of this command we'd be very appreciative!\
158+
quoting the full output of this command we'd be very appreciative!\n\
159+
\n\
160+
The following errors were reported:\n\
161+
error: expected one of `!` or `::`, found `rust`\n\
148162
",
149163
)
164+
.with_stderr_contains("Original diagnostics will follow.")
165+
.with_stderr_contains("[WARNING] variable does not need to be mutable")
150166
.with_stderr_does_not_contain("[..][FIXING][..]")
151167
.run();
168+
169+
// Make sure the fix which should have been applied was backed out
170+
assert!(p.read_file("bar/src/lib.rs").contains("let mut x = 3;"));
152171
}
153172

154173
#[test]

0 commit comments

Comments
 (0)