Skip to content

Commit 5c67257

Browse files
author
Thomas Bracht Laumann Jespersen
authored
Merge pull request Manishearth#151 from phansch/add_rustfix_support
Add rustfix support from rustc (rough rustc copy)
2 parents 25b681f + 9779f6c commit 5c67257

File tree

10 files changed

+191
-7
lines changed

10 files changed

+191
-7
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ tempfile = { version = "3.0", optional = true }
2424
serde = "1.0"
2525
serde_json = "1.0"
2626
serde_derive = "1.0"
27+
rustfix = "0.4.1"
2728
tester = { version = "0.5", optional = true }
2829

2930
[target."cfg(unix)".dependencies]

src/common.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ pub struct TestPaths {
234234
pub relative_dir: PathBuf, // e.g., foo/bar
235235
}
236236

237+
pub const UI_STDERR: &str = "stderr";
238+
pub const UI_STDOUT: &str = "stdout";
239+
pub const UI_FIXED: &str = "fixed";
240+
237241
impl Config {
238242
/// Add rustc flags to link with the crate's dependencies in addition to the crate itself
239243
pub fn link_deps(&mut self) {

src/header.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ pub struct TestProps {
222222
// customized normalization rules
223223
pub normalize_stdout: Vec<(String, String)>,
224224
pub normalize_stderr: Vec<(String, String)>,
225+
pub run_rustfix: bool,
226+
pub rustfix_only_machine_applicable: bool,
225227
}
226228

227229
impl TestProps {
@@ -250,6 +252,8 @@ impl TestProps {
250252
run_pass: false,
251253
normalize_stdout: vec![],
252254
normalize_stderr: vec![],
255+
run_rustfix: false,
256+
rustfix_only_machine_applicable: false,
253257
}
254258
}
255259

@@ -371,6 +375,15 @@ impl TestProps {
371375
if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") {
372376
self.normalize_stderr.push(rule);
373377
}
378+
379+
if !self.run_rustfix {
380+
self.run_rustfix = config.parse_run_rustfix(ln);
381+
}
382+
383+
if !self.rustfix_only_machine_applicable {
384+
self.rustfix_only_machine_applicable =
385+
config.parse_rustfix_only_machine_applicable(ln);
386+
}
374387
});
375388

376389
for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] {
@@ -588,6 +601,14 @@ impl Config {
588601

589602
None
590603
}
604+
605+
fn parse_run_rustfix(&self, line: &str) -> bool {
606+
self.parse_name_directive(line, "run-rustfix")
607+
}
608+
609+
fn parse_rustfix_only_machine_applicable(&self, line: &str) -> bool {
610+
self.parse_name_directive(line, "rustfix-only-machine-applicable")
611+
}
591612
}
592613

593614
pub fn lldb_version_to_int(version_string: &str) -> isize {

src/json.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct Diagnostic {
2424
level: String,
2525
spans: Vec<DiagnosticSpan>,
2626
children: Vec<Diagnostic>,
27+
rendered: Option<String>,
2728
}
2829

2930
#[derive(Deserialize, Clone)]
@@ -71,6 +72,28 @@ struct DiagnosticCode {
7172
explanation: Option<String>,
7273
}
7374

75+
pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String {
76+
output
77+
.lines()
78+
.filter_map(|line| {
79+
if line.starts_with('{') {
80+
match serde_json::from_str::<Diagnostic>(line) {
81+
Ok(diagnostic) => diagnostic.rendered,
82+
Err(error) => {
83+
proc_res.fatal(Some(&format!(
84+
"failed to decode compiler output as json: \
85+
`{}`\nline: {}\noutput: {}",
86+
error, line, output
87+
)));
88+
}
89+
}
90+
} else {
91+
None
92+
}
93+
})
94+
.collect()
95+
}
96+
7497
pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec<Error> {
7598
output.lines()
7699
.flat_map(|line| parse_line(file_name, line, output, proc_res))

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ extern crate diff;
3232
extern crate serde_json;
3333
#[macro_use]
3434
extern crate serde_derive;
35+
extern crate rustfix;
3536

3637
use std::env;
3738
use std::ffi::OsString;

src/runtest.rs

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
use common::{Config, TestPaths};
12+
use common::{UI_FIXED, UI_STDERR, UI_STDOUT};
1213
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
1314
use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
1415
use common::{Incremental, RunMake, Ui, MirOpt};
@@ -17,6 +18,7 @@ use errors::{self, ErrorKind, Error};
1718
use filetime::FileTime;
1819
use json;
1920
use regex::Regex;
21+
use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
2022
use header::TestProps;
2123
use util::logv;
2224

@@ -1430,7 +1432,16 @@ actual:\n\
14301432

14311433
rustc.arg(dir_opt);
14321434
}
1433-
RunPass |
1435+
RunPass | Ui => {
1436+
if !self
1437+
.props
1438+
.compile_flags
1439+
.iter()
1440+
.any(|s| s.starts_with("--error-format"))
1441+
{
1442+
rustc.args(&["--error-format", "json"]);
1443+
}
1444+
}
14341445
RunFail |
14351446
RunPassValgrind |
14361447
Pretty |
@@ -1439,7 +1450,6 @@ actual:\n\
14391450
Codegen |
14401451
Rustdoc |
14411452
RunMake |
1442-
Ui |
14431453
CodegenUnits => {
14441454
// do not use JSON output
14451455
}
@@ -2222,22 +2232,68 @@ actual:\n\
22222232
}
22232233

22242234
fn run_ui_test(&self) {
2235+
// if the user specified a format in the ui test
2236+
// print the output to the stderr file, otherwise extract
2237+
// the rendered error messages from json and print them
2238+
let explicit = self
2239+
.props
2240+
.compile_flags
2241+
.iter()
2242+
.any(|s| s.contains("--error-format"));
22252243
let proc_res = self.compile_test();
22262244

2227-
let expected_stderr_path = self.expected_output_path("stderr");
2245+
let expected_stderr_path = self.expected_output_path(UI_STDERR);
22282246
let expected_stderr = self.load_expected_output(&expected_stderr_path);
22292247

2230-
let expected_stdout_path = self.expected_output_path("stdout");
2248+
let expected_stdout_path = self.expected_output_path(UI_STDOUT);
22312249
let expected_stdout = self.load_expected_output(&expected_stdout_path);
22322250

2251+
let expected_fixed_path = self.expected_output_path(UI_FIXED);
2252+
let expected_fixed = self.load_expected_output(&expected_fixed_path);
2253+
22332254
let normalized_stdout =
22342255
self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout);
2256+
2257+
let stderr = if explicit {
2258+
proc_res.stderr.clone()
2259+
} else {
2260+
json::extract_rendered(&proc_res.stderr, &proc_res)
2261+
};
2262+
22352263
let normalized_stderr =
2236-
self.normalize_output(&proc_res.stderr, &self.props.normalize_stderr);
2264+
self.normalize_output(&stderr, &self.props.normalize_stderr);
22372265

22382266
let mut errors = 0;
2239-
errors += self.compare_output("stdout", &normalized_stdout, &expected_stdout);
2240-
errors += self.compare_output("stderr", &normalized_stderr, &expected_stderr);
2267+
errors += self.compare_output(UI_STDOUT, &normalized_stdout, &expected_stdout);
2268+
errors += self.compare_output(UI_STDERR, &normalized_stderr, &expected_stderr);
2269+
2270+
2271+
if self.props.run_rustfix {
2272+
// Apply suggestions from lints to the code itself
2273+
let unfixed_code = self
2274+
.load_expected_output_from_path(&self.testpaths.file)
2275+
.expect("Could not load output from path");
2276+
let suggestions = get_suggestions_from_json(
2277+
&proc_res.stderr,
2278+
&HashSet::new(),
2279+
if self.props.rustfix_only_machine_applicable {
2280+
Filter::MachineApplicableOnly
2281+
} else {
2282+
Filter::Everything
2283+
},
2284+
).expect("Could not retrieve suggestions from JSON");
2285+
let fixed_code = apply_suggestions(&unfixed_code, &suggestions).expect(&format!(
2286+
"failed to apply suggestions for {:?} with rustfix",
2287+
self.testpaths.file
2288+
));
2289+
2290+
errors += self.compare_output(UI_FIXED, &fixed_code, &expected_fixed);
2291+
} else if !expected_fixed.is_empty() {
2292+
panic!(
2293+
"the `// run-rustfix` directive wasn't found but a `*.fixed` \
2294+
file was found"
2295+
);
2296+
}
22412297

22422298
if errors > 0 {
22432299
println!("To update references, run this command from build directory:");
@@ -2259,6 +2315,23 @@ actual:\n\
22592315
self.fatal_proc_rec("test run failed!", &proc_res);
22602316
}
22612317
}
2318+
2319+
if self.props.run_rustfix {
2320+
// And finally, compile the fixed code and make sure it both
2321+
// succeeds and has no diagnostics.
2322+
let mut rustc = self.make_compile_args(
2323+
&self.testpaths.file.with_extension(UI_FIXED),
2324+
TargetLocation::ThisFile(self.make_exe_name()),
2325+
);
2326+
rustc.arg("-L").arg(&self.aux_output_dir_name());
2327+
let res = self.compose_and_run_compiler(rustc, None);
2328+
if !res.status.success() {
2329+
self.fatal_proc_rec("failed to compile fixed code", &res);
2330+
}
2331+
if !res.stderr.is_empty() && !self.props.rustfix_only_machine_applicable {
2332+
self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
2333+
}
2334+
}
22622335
}
22632336

22642337
fn run_mir_opt_test(&self) {
@@ -2487,6 +2560,12 @@ actual:\n\
24872560
}
24882561
}
24892562

2563+
fn load_expected_output_from_path(&self, path: &Path) -> Result<String, String> {
2564+
fs::read_to_string(path).map_err(|err| {
2565+
format!("failed to load expected output from `{}`: {}", path.display(), err)
2566+
})
2567+
}
2568+
24902569
fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
24912570
if actual == expected {
24922571
return 0;

test-project/tests/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ fn run_mode(mode: &'static str) {
1919
fn compile_test() {
2020
run_mode("compile-fail");
2121
run_mode("run-pass");
22+
run_mode("ui");
2223

2324
#[cfg(not(feature = "stable"))]
2425
run_mode("pretty");
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// run-rustfix
12+
13+
#![allow(unused_variables)]
14+
#![deny(keyword_idents)]
15+
16+
fn main() {
17+
let r#dyn = (); //~ ERROR dyn
18+
//~^ WARN hard error in the 2018 edition
19+
}

test-project/tests/ui/dyn-keyword.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// run-rustfix
12+
13+
#![allow(unused_variables)]
14+
#![deny(keyword_idents)]
15+
16+
fn main() {
17+
let dyn = (); //~ ERROR dyn
18+
//~^ WARN hard error in the 2018 edition
19+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: `dyn` is a keyword in the 2018 edition
2+
--> $DIR/dyn-keyword.rs:17:9
3+
|
4+
17 | let dyn = (); //~ ERROR dyn
5+
| ^^^ help: you can use a raw identifier to stay compatible: `r#dyn`
6+
|
7+
note: lint level defined here
8+
--> $DIR/dyn-keyword.rs:14:9
9+
|
10+
14 | #![deny(keyword_idents)]
11+
| ^^^^^^^^^^^^^^
12+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
13+
= note: for more information, see issue #49716 <https://github.com/rust-lang/rust/issues/49716>
14+
15+
error: aborting due to previous error
16+

0 commit comments

Comments
 (0)