From 3e746aefa6c899f0620670c2bd66b0fe2b7e79b1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 13 Nov 2018 12:08:57 -0800 Subject: [PATCH] fix: Don't back out changes with `--broken-code` This commit updates the behavior of `cargo fix` when the `--broken-code` flag is passed to Cargo. Previously Cargo would always back out automatically applied changes to files whenever the fixed code failed to compile. Now, with the `--broken-code` flag, fixed code is left as-is. This means that if the fixed code can be more easily inspected by humans to detect bugs and such. The main use case intended here is that if you're working with a large code base then lints like the edition idiom lints aren't 100% finished yet to work as smoothly as `cargo fix`. The idiom lints are often useful, however, to transition code to be idiomatic (who would have guessed!) in the new edition. To ease the experience of using not-quite-ready lints this flag can be used to hopefully "fix 90% of lint warnings" and then the remaining compiler errors can be sifted through manually. The intention is that we have edition documentation indicating this workflow which also encourages filing bugs for anything that fails to fix, and hopefully this new behavior will make it easier for us to narrow down what the minimal test case is too! --- src/cargo/ops/fix.rs | 8 +++-- tests/testsuite/fix.rs | 71 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index 2e09303a1ac..2c1a9cf445a 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -205,9 +205,11 @@ pub fn fix_maybe_exec_rustc() -> CargoResult { // user's code with our changes. Back out everything and fall through // below to recompile again. if !output.status.success() { - for (path, file) in fixes.files.iter() { - fs::write(path, &file.original_code) - .with_context(|_| format!("failed to write file `{}`", path))?; + if env::var_os(BROKEN_CODE_ENV).is_none() { + for (path, file) in fixes.files.iter() { + fs::write(path, &file.original_code) + .with_context(|_| format!("failed to write file `{}`", path))?; + } } log_failed_fix(&output.stderr)?; } diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index d37688368f0..0666e2c93e6 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -1116,3 +1116,74 @@ fn only_warn_for_relevant_crates() { ") .run(); } + +#[test] +fn fix_to_broken_code() { + if !is_nightly() { + return; + } + let p = project() + .file( + "foo/Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + [workspace] + "#, + ).file( + "foo/src/main.rs", + r##" + use std::env; + use std::fs; + use std::io::Write; + use std::path::{Path, PathBuf}; + use std::process::{self, Command}; + + fn main() { + let is_lib_rs = env::args_os() + .map(PathBuf::from) + .any(|l| l == Path::new("src/lib.rs")); + if is_lib_rs { + let path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let path = path.join("foo"); + if path.exists() { + panic!() + } else { + fs::File::create(&path).unwrap(); + } + } + + let status = Command::new("rustc") + .args(env::args().skip(1)) + .status() + .expect("failed to run rustc"); + process::exit(status.code().unwrap_or(2)); + } + "##, + ).file( + "bar/Cargo.toml", + r#" + [package] + name = 'bar' + version = '0.1.0' + [workspace] + "#, + ).file("bar/build.rs", "fn main() {}") + .file( + "bar/src/lib.rs", + "pub fn foo() { let mut x = 3; drop(x); }", + ).build(); + + // Build our rustc shim + p.cargo("build").cwd(p.root().join("foo")).run(); + + // Attempt to fix code, but our shim will always fail the second compile + p.cargo("fix --allow-no-vcs --broken-code") + .cwd(p.root().join("bar")) + .env("RUSTC", p.root().join("foo/target/debug/foo")) + .with_status(101) + .run(); + + assert_eq!(p.read_file("bar/src/lib.rs"), "pub fn foo() { let x = 3; drop(x); }"); +}