Skip to content
This repository was archived by the owner on Nov 24, 2023. It is now read-only.

Commit 7d5bb22

Browse files
committed
Add filtering by machine applicability
1 parent f6e423f commit 7d5bb22

14 files changed

+96
-31
lines changed

cargo-fix/src/cli.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ pub fn run() -> Result<(), Error> {
112112
//
113113
// TODO: we probably want to do something fancy here like collect results
114114
// from the client processes and print out a summary of what happened.
115-
let status = cmd
116-
.status()
115+
let status = cmd.status()
117116
.with_context(|e| format!("failed to execute `{}`: {}", cargo.to_string_lossy(), e))?;
118117
exit_with(status);
119118
}

cargo-fix/src/main.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ use std::str;
2121

2222
use failure::{Error, ResultExt};
2323
use rustfix::diagnostics::Diagnostic;
24-
use termcolor::{ColorSpec, WriteColor, Color};
24+
use termcolor::{Color, ColorSpec, WriteColor};
2525

26-
use diagnostics::{Message, output_stream};
26+
use diagnostics::{output_stream, Message};
2727

2828
mod cli;
2929
mod diagnostics;
@@ -174,6 +174,10 @@ fn rustfix_crate(rustc: &Path, filename: &str) -> Result<FixedCrate, Error> {
174174
return Ok(Default::default());
175175
}
176176

177+
let fix_mode = env::var_os("__CARGO_FIX_YOLO")
178+
.map(|_| rustfix::Filter::Everything)
179+
.unwrap_or(rustfix::Filter::MachineApplicableOnly);
180+
177181
// Sift through the output of the compiler to look for JSON messages
178182
// indicating fixes that we can apply.
179183
let stderr = str::from_utf8(&output.stderr).context("failed to parse rustc stderr as utf-8")?;
@@ -185,7 +189,7 @@ fn rustfix_crate(rustc: &Path, filename: &str) -> Result<FixedCrate, Error> {
185189
.filter_map(|line| serde_json::from_str::<Diagnostic>(line).ok())
186190

187191
// From each diagnostic try to extract suggestions from rustc
188-
.filter_map(|diag| rustfix::collect_suggestions(&diag, &only));
192+
.filter_map(|diag| rustfix::collect_suggestions(&diag, &only, fix_mode));
189193

190194
// Collect suggestions by file so we can apply them one at a time later.
191195
let mut file_map = HashMap::new();

cargo-fix/tests/all/broken_build.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ fn fix_broken_if_requested() {
3636
)
3737
.build();
3838

39-
p.expect_cmd("cargo-fix fix --broken-code").status(0).run();
39+
p.expect_cmd("cargo-fix fix --broken-code")
40+
.env("__CARGO_FIX_YOLO", "true")
41+
.status(0)
42+
.run();
4043
}
4144

4245
#[test]
@@ -111,6 +114,7 @@ fn broken_fixes_backed_out() {
111114

112115
// Attempt to fix code, but our shim will always fail the second compile
113116
p.expect_cmd("cargo-fix fix")
117+
.env("__CARGO_FIX_YOLO", "true")
114118
.cwd("bar")
115119
.env("RUSTC", p.root.join("foo/target/debug/foo"))
116120
.stderr_contains("not rust code")

cargo-fix/tests/all/broken_lints.rs

+10-13
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,18 @@
1919
// .build();
2020

2121
// p.expect_cmd("cargo-fix fix")
22+
// .env("__CARGO_FIX_YOLO", "true")
2223
// .stderr_contains(r"warning: error applying suggestions to `src/lib.rs`")
2324
// .stderr_contains("The full error message was:")
24-
// .stderr_contains(
25-
// "> Could not replace range 56...60 in file -- maybe parts of it were already replaced?",
26-
// )
27-
// .stderr_contains(
28-
// "\
29-
// This likely indicates a bug in either rustc or rustfix itself,\n\
30-
// and we would appreciate a bug report! You're likely to see \n\
31-
// a number of compiler warnings after this message which rustfix\n\
32-
// attempted to fix but failed. If you could open an issue at\n\
33-
// https://github.com/rust-lang-nursery/rustfix/issues\n\
34-
// quoting the full output of this command we'd be very appreciative!\n\n\
35-
// ",
36-
// )
25+
// .stderr_contains("> Could not replace range 56...60 in file -- maybe parts of it were already replaced?")
26+
// .stderr_contains("\
27+
// This likely indicates a bug in either rustc or rustfix itself,\n\
28+
// and we would appreciate a bug report! You're likely to see \n\
29+
// a number of compiler warnings after this message which rustfix\n\
30+
// attempted to fix but failed. If you could open an issue at\n\
31+
// https://github.com/rust-lang-nursery/rustfix/issues\n\
32+
// quoting the full output of this command we'd be very appreciative!\n\n\
33+
// ")
3734
// .status(0)
3835
// .run();
3936
// }

cargo-fix/tests/all/dependencies.rs

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ fn fix_path_deps() {
5454
[FINISHED] dev [unoptimized + debuginfo]
5555
";
5656
p.expect_cmd("cargo-fix fix")
57+
.env("__CARGO_FIX_YOLO", "true")
5758
.stdout("")
5859
.stderr(stderr)
5960
.run();

cargo-fix/tests/all/edition_upgrade.rs

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ fn local_paths() {
7272
[FINISHED] dev [unoptimized + debuginfo]
7373
";
7474
p.expect_cmd("cargo-fix fix --prepare-for 2018")
75+
.env("__CARGO_FIX_YOLO", "true")
7576
.stdout("")
7677
.stderr(stderr)
7778
.run();
@@ -104,6 +105,7 @@ fn local_paths_no_fix() {
104105
[FINISHED] dev [unoptimized + debuginfo]
105106
";
106107
p.expect_cmd("cargo-fix fix --prepare-for 2018")
108+
.env("__CARGO_FIX_YOLO", "true")
107109
.stdout("")
108110
.stderr(stderr)
109111
.run();
@@ -161,6 +163,7 @@ fn upgrade_extern_crate() {
161163
[FINISHED] dev [unoptimized + debuginfo]
162164
";
163165
p.expect_cmd("cargo-fix fix")
166+
.env("__CARGO_FIX_YOLO", "true")
164167
.stdout("")
165168
.stderr(stderr)
166169
.run();

cargo-fix/tests/all/smoke.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ fn fixes_extra_mut() {
3434
[FINISHED] dev [unoptimized + debuginfo]
3535
";
3636
p.expect_cmd("cargo-fix fix")
37+
.env("__CARGO_FIX_YOLO", "true")
3738
.stdout("")
3839
.stderr(stderr)
3940
.run();
@@ -60,6 +61,7 @@ fn fixes_two_missing_ampersands() {
6061
[FINISHED] dev [unoptimized + debuginfo]
6162
";
6263
p.expect_cmd("cargo-fix fix")
64+
.env("__CARGO_FIX_YOLO", "true")
6365
.stdout("")
6466
.stderr(stderr)
6567
.run();
@@ -85,6 +87,7 @@ fn tricky() {
8587
[FINISHED] dev [unoptimized + debuginfo]
8688
";
8789
p.expect_cmd("cargo-fix fix")
90+
.env("__CARGO_FIX_YOLO", "true")
8891
.stdout("")
8992
.stderr(stderr)
9093
.run();
@@ -102,7 +105,9 @@ fn preserve_line_endings() {
102105
)
103106
.build();
104107

105-
p.expect_cmd("cargo-fix fix").run();
108+
p.expect_cmd("cargo-fix fix")
109+
.env("__CARGO_FIX_YOLO", "true")
110+
.run();
106111
assert!(p.read("src/lib.rs").contains("\r\n"));
107112
}
108113

@@ -118,7 +123,9 @@ fn fix_deny_warnings() {
118123
)
119124
.build();
120125

121-
p.expect_cmd("cargo-fix fix").run();
126+
p.expect_cmd("cargo-fix fix")
127+
.env("__CARGO_FIX_YOLO", "true")
128+
.run();
122129
}
123130

124131
#[test]
@@ -139,7 +146,9 @@ fn fix_deny_warnings_but_not_others() {
139146
)
140147
.build();
141148

142-
p.expect_cmd("cargo-fix fix").run();
149+
p.expect_cmd("cargo-fix fix")
150+
.env("__CARGO_FIX_YOLO", "true")
151+
.run();
143152
assert!(!p.read("src/lib.rs").contains("let mut x = 3;"));
144153
assert!(p.read("src/lib.rs").contains("fn bar() {}"));
145154
}
@@ -170,6 +179,7 @@ fn fix_two_files() {
170179
.build();
171180

172181
p.expect_cmd("cargo-fix fix")
182+
.env("__CARGO_FIX_YOLO", "true")
173183
.stderr_contains("[FIXING] src/bar.rs (1 fix)")
174184
.stderr_contains("[FIXING] src/lib.rs (1 fix)")
175185
.run();

cargo-fix/tests/all/subtargets.rs

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ fn fixes_missing_ampersand() {
4040
.build();
4141

4242
p.expect_cmd("cargo fix -- --all-targets")
43+
.env("__CARGO_FIX_YOLO", "true")
4344
.stdout("")
4445
.stderr_contains("[COMPILING] foo v0.1.0 (CWD)")
4546
.stderr_contains("[FIXING] build.rs (1 fix)")

cargo-fix/tests/all/vcs.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,8 @@ fn does_not_warn_about_clean_working_directory() {
7171
.run();
7272
p.expect_cmd("git config user.name RustFix").run();
7373
p.expect_cmd("git commit -m Initial-commit").run();
74-
p.expect_cmd("cargo-fix fix").check_vcs(true).status(0).run();
74+
p.expect_cmd("cargo-fix fix")
75+
.check_vcs(true)
76+
.status(0)
77+
.run();
7578
}

examples/fix-json.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ fn main() -> Result<(), Error> {
1515
};
1616

1717
let suggestions = fs::read_to_string(&suggestions_file)?;
18-
let suggestions = rustfix::get_suggestions_from_json(&suggestions, &HashSet::new())?;
18+
let suggestions = rustfix::get_suggestions_from_json(
19+
&suggestions,
20+
&HashSet::new(),
21+
rustfix::Filter::Everything,
22+
)?;
1923

2024
let source = fs::read_to_string(&source_file)?;
2125

src/diagnostics.rs

+9
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,19 @@ pub struct DiagnosticSpan {
4141
/// load the fully rendered version from the parent `Diagnostic`,
4242
/// however.
4343
pub suggested_replacement: Option<String>,
44+
pub suggestion_applicability: Option<Applicability>,
4445
/// Macro invocations that created the code at this span, if any.
4546
expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
4647
}
4748

49+
#[derive(Copy, Clone, Debug, PartialEq, Deserialize)]
50+
pub enum Applicability {
51+
MachineApplicable,
52+
HasPlaceholders,
53+
MaybeIncorrect,
54+
Unspecified,
55+
}
56+
4857
#[derive(Deserialize, Debug)]
4958
pub struct DiagnosticSpanLine {
5059
pub text: String,

src/lib.rs

+24-2
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,21 @@ pub mod diagnostics;
1818
use diagnostics::{Diagnostic, DiagnosticSpan};
1919
mod replace;
2020

21+
#[derive(Debug, Clone, Copy)]
22+
pub enum Filter {
23+
MachineApplicableOnly,
24+
Everything,
25+
}
26+
2127
pub fn get_suggestions_from_json<S: ::std::hash::BuildHasher>(
2228
input: &str,
2329
only: &HashSet<String, S>,
30+
filter: Filter,
2431
) -> serde_json::error::Result<Vec<Suggestion>> {
2532
let mut result = Vec::new();
2633
for cargo_msg in serde_json::Deserializer::from_str(input).into_iter::<Diagnostic>() {
2734
// One diagnostic line might have multiple suggestions
28-
result.extend(collect_suggestions(&cargo_msg?, only));
35+
result.extend(collect_suggestions(&cargo_msg?, only, filter));
2936
}
3037
Ok(result)
3138
}
@@ -142,6 +149,7 @@ fn collect_span(span: &DiagnosticSpan) -> Option<Replacement> {
142149
pub fn collect_suggestions<S: ::std::hash::BuildHasher>(
143150
diagnostic: &Diagnostic,
144151
only: &HashSet<String, S>,
152+
filter: Filter,
145153
) -> Option<Suggestion> {
146154
if !only.is_empty() {
147155
if let Some(ref code) = diagnostic.code {
@@ -165,7 +173,21 @@ pub fn collect_suggestions<S: ::std::hash::BuildHasher>(
165173
.children
166174
.iter()
167175
.filter_map(|child| {
168-
let replacements: Vec<_> = child.spans.iter().filter_map(collect_span).collect();
176+
let replacements: Vec<_> = child
177+
.spans
178+
.iter()
179+
.filter(|span| {
180+
use Filter::*;
181+
use diagnostics::Applicability::*;
182+
183+
match (filter, &span.suggestion_applicability) {
184+
(MachineApplicableOnly, Some(MachineApplicable)) => true,
185+
(MachineApplicableOnly, _) => false,
186+
(_, _) => true,
187+
}
188+
})
189+
.filter_map(collect_span)
190+
.collect();
169191
if replacements.len() == 1 {
170192
Some(Solution {
171193
message: child.message.clone(),

tests/edge_cases.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::fs;
55
#[test]
66
fn multiple_fix_options_yield_no_suggestions() {
77
let json = fs::read_to_string("./tests/edge-cases/skip-multi-option-lints.json").unwrap();
8-
let expected_suggestions = rustfix::get_suggestions_from_json(&json, &HashSet::new()).unwrap();
8+
let expected_suggestions =
9+
rustfix::get_suggestions_from_json(&json, &HashSet::new(), rustfix::Filter::Everything)
10+
.unwrap();
911
assert!(expected_suggestions.is_empty());
1012
}

tests/parse_and_replace.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ fn compile(file: &Path, mode: &str) -> Result<Output, Error> {
4343
"-Zunstable-options".into(),
4444
"--emit=metadata".into(),
4545
"--crate-name=rustfix_test".into(),
46-
"-Zsuggestion-applicability".into(),
4746
"--out-dir".into(),
4847
tmp.path().into(),
4948
];
@@ -142,12 +141,19 @@ fn test_rustfix_with_file<P: AsRef<Path>>(file: P, mode: &str) -> Result<(), Err
142141
let json_file = file.with_extension("json");
143142
let fixed_file = file.with_extension("fixed.rs");
144143

144+
let filter_suggestions = if mode == fixmode::EVERYTHING {
145+
rustfix::Filter::Everything
146+
} else {
147+
rustfix::Filter::MachineApplicableOnly
148+
};
149+
145150
debug!("next up: {:?}", file);
146151
let code = read_file(file).context(format!("could not read {}", file.display()))?;
147152
let errors = compile_and_get_json_errors(file, mode)
148153
.context(format!("could compile {}", file.display()))?;
149-
let suggestions = rustfix::get_suggestions_from_json(&errors, &HashSet::new())
150-
.context("could not load suggestions")?;
154+
let suggestions =
155+
rustfix::get_suggestions_from_json(&errors, &HashSet::new(), filter_suggestions)
156+
.context("could not load suggestions")?;
151157

152158
if std::env::var(settings::RECORD_JSON).is_ok() {
153159
use std::io::Write;
@@ -163,7 +169,7 @@ fn test_rustfix_with_file<P: AsRef<Path>>(file: P, mode: &str) -> Result<(), Err
163169
file.display()
164170
))?;
165171
let expected_suggestions =
166-
rustfix::get_suggestions_from_json(&expected_json, &HashSet::new())
172+
rustfix::get_suggestions_from_json(&expected_json, &HashSet::new(), filter_suggestions)
167173
.context("could not load expected suggesitons")?;
168174

169175
ensure!(

0 commit comments

Comments
 (0)