From b81c45609167568443b6f7dcf812b06df507da62 Mon Sep 17 00:00:00 2001 From: mcarton Date: Fri, 23 Sep 2016 19:31:01 +0200 Subject: [PATCH] Rustup to *rustc 1.13.0-nightly (4f9812a59 2016-09-21)* --- Cargo.toml | 2 +- src/common.rs | 67 +++++++++-------- src/compiletest.rs | 92 ++++++++++++++--------- src/errors.rs | 79 +++++++++++--------- src/header.rs | 122 +++++++++++++++++-------------- src/json.rs | 92 +++++++++++------------ src/procsrv.rs | 49 +++++++------ src/runtest.rs | 177 +++++++++++++++++++++++++++++++++++++++------ src/uidiff.rs | 45 ++++++------ src/util.rs | 86 +++++++++++----------- 10 files changed, 493 insertions(+), 318 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f28146..50cec8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "compiletest_rs" -version = "0.2.1" +version = "0.2.2" authors = [ "The Rust Project Developers" , "Thomas Bracht Laumann Jespersen " , "Manish Goregaokar " diff --git a/src/common.rs b/src/common.rs index 5ec62e0..5d52273 100644 --- a/src/common.rs +++ b/src/common.rs @@ -29,27 +29,29 @@ pub enum Mode { Incremental, RunMake, Ui, + MirOpt, } impl FromStr for Mode { type Err = (); fn from_str(s: &str) -> Result { match s { - "compile-fail" => Ok(CompileFail), - "parse-fail" => Ok(ParseFail), - "run-fail" => Ok(RunFail), - "run-pass" => Ok(RunPass), - "run-pass-valgrind" => Ok(RunPassValgrind), - "pretty" => Ok(Pretty), - "debuginfo-lldb" => Ok(DebugInfoLldb), - "debuginfo-gdb" => Ok(DebugInfoGdb), - "codegen" => Ok(Codegen), - "rustdoc" => Ok(Rustdoc), - "codegen-units" => Ok(CodegenUnits), - "incremental" => Ok(Incremental), - "run-make" => Ok(RunMake), - "ui" => Ok(Ui), - _ => Err(()), + "compile-fail" => Ok(CompileFail), + "parse-fail" => Ok(ParseFail), + "run-fail" => Ok(RunFail), + "run-pass" => Ok(RunPass), + "run-pass-valgrind" => Ok(RunPassValgrind), + "pretty" => Ok(Pretty), + "debuginfo-lldb" => Ok(DebugInfoLldb), + "debuginfo-gdb" => Ok(DebugInfoGdb), + "codegen" => Ok(Codegen), + "rustdoc" => Ok(Rustdoc), + "codegen-units" => Ok(CodegenUnits), + "incremental" => Ok(Incremental), + "run-make" => Ok(RunMake), + "ui" => Ok(Ui), + "mir-opt" => Ok(MirOpt), + _ => Err(()), } } } @@ -57,21 +59,23 @@ impl FromStr for Mode { impl fmt::Display for Mode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(match *self { - CompileFail => "compile-fail", - ParseFail => "parse-fail", - RunFail => "run-fail", - RunPass => "run-pass", - RunPassValgrind => "run-pass-valgrind", - Pretty => "pretty", - DebugInfoGdb => "debuginfo-gdb", - DebugInfoLldb => "debuginfo-lldb", - Codegen => "codegen", - Rustdoc => "rustdoc", - CodegenUnits => "codegen-units", - Incremental => "incremental", - RunMake => "run-make", - Ui => "ui", - }, f) + CompileFail => "compile-fail", + ParseFail => "parse-fail", + RunFail => "run-fail", + RunPass => "run-pass", + RunPassValgrind => "run-pass-valgrind", + Pretty => "pretty", + DebugInfoGdb => "debuginfo-gdb", + DebugInfoLldb => "debuginfo-lldb", + Codegen => "codegen", + Rustdoc => "rustdoc", + CodegenUnits => "codegen-units", + Incremental => "incremental", + RunMake => "run-make", + Ui => "ui", + MirOpt => "mir-opt", + }, + f) } } @@ -148,6 +152,9 @@ pub struct Config { // Version of LLDB pub lldb_version: Option, + // Version of LLVM + pub llvm_version: Option, + // Path to the android tools pub android_cross_path: PathBuf, diff --git a/src/compiletest.rs b/src/compiletest.rs index f753970..e805911 100644 --- a/src/compiletest.rs +++ b/src/compiletest.rs @@ -23,6 +23,7 @@ extern crate rustc_serialize; extern crate log; use std::env; +use std::ffi::OsString; use std::fs; use std::io; use std::path::{Path, PathBuf}; @@ -68,6 +69,7 @@ pub fn default_config() -> Config { host: "(none)".to_owned(), gdb_version: None, lldb_version: None, + llvm_version: None, android_cross_path: PathBuf::from("android-cross-path"), adb_path: "adb-path".to_owned(), adb_test_dir: "adb-test-dir/target".to_owned(), @@ -85,9 +87,9 @@ pub fn default_config() -> Config { pub fn run_tests(config: &Config) { if config.target.contains("android") { - if config.mode == DebugInfoGdb { + if let DebugInfoGdb = config.mode { println!("{} debug-info test uses tcp 5039 port.\ - please reserve it", config.target); + please reserve it", config.target); } // android debug-info test uses remote debugger @@ -162,18 +164,39 @@ fn collect_tests_from_dir(config: &Config, // `compiletest-ignore-dir`. for file in try!(fs::read_dir(dir)) { let file = try!(file); - if file.file_name() == *"compiletest-ignore-dir" { + let name = file.file_name(); + if name == *"compiletest-ignore-dir" { return Ok(()); } + if name == *"Makefile" && config.mode == Mode::RunMake { + let paths = TestPaths { + file: dir.to_path_buf(), + base: base.to_path_buf(), + relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), + }; + tests.push(make_test(config, &paths)); + return Ok(()) + } } + // If we find a test foo/bar.rs, we have to build the + // output directory `$build/foo` so we can write + // `$build/foo/bar` into it. We do this *now* in this + // sequential loop because otherwise, if we do it in the + // tests themselves, they race for the privilege of + // creating the directories and sometimes fail randomly. + let build_dir = config.build_base.join(&relative_dir_path); + fs::create_dir_all(&build_dir).unwrap(); + + // Add each `.rs` file as a test, and recurse further on any + // subdirectories we find, except for `aux` directories. let dirs = try!(fs::read_dir(dir)); for file in dirs { let file = try!(file); let file_path = file.path(); - debug!("inspecting file {:?}", file_path.display()); - if is_test(config, &file_path) { - // If we find a test foo/bar.rs, we have to build the + let file_name = file.file_name(); + if is_test(&file_name) { + debug!("found test file: {:?}", file_path.display()); // output directory `$build/foo` so we can write // `$build/foo/bar` into it. We do this *now* in this // sequential loop because otherwise, if we do it in the @@ -190,41 +213,39 @@ fn collect_tests_from_dir(config: &Config, tests.push(make_test(config, &paths)) } else if file_path.is_dir() { let relative_file_path = relative_dir_path.join(file.file_name()); - try!(collect_tests_from_dir(config, - base, - &file_path, - &relative_file_path, - tests)); + if &file_name == "auxiliary" { + // `aux` directories contain other crates used for + // cross-crate tests. Don't search them for tests, but + // do create a directory in the build dir for them, + // since we will dump intermediate output in there + // sometimes. + let build_dir = config.build_base.join(&relative_file_path); + fs::create_dir_all(&build_dir).unwrap(); + } else { + debug!("found directory: {:?}", file_path.display()); + try!(collect_tests_from_dir(config, + base, + &file_path, + &relative_file_path, + tests)); + } + } else { + debug!("found other file/directory: {:?}", file_path.display()); } } Ok(()) } -pub fn is_test(config: &Config, testfile: &Path) -> bool { - // Pretty-printer does not work with .rc files yet - let valid_extensions = - match config.mode { - Pretty => vec!(".rs".to_owned()), - _ => vec!(".rc".to_owned(), ".rs".to_owned()) - }; - let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned()); - let name = testfile.file_name().unwrap().to_str().unwrap(); - - let mut valid = false; - - for ext in &valid_extensions { - if name.ends_with(ext) { - valid = true; - } - } +pub fn is_test(file_name: &OsString) -> bool { + let file_name = file_name.to_str().unwrap(); - for pre in &invalid_prefixes { - if name.starts_with(pre) { - valid = false; - } + if !file_name.ends_with(".rs") { + return false; } - valid + // `.`, `#`, and `~` are common temp-file prefixes. + let invalid_prefixes = &[".", "#", "~"]; + !invalid_prefixes.iter().any(|p| file_name.starts_with(p)) } pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn { @@ -352,3 +373,8 @@ fn extract_lldb_version(full_version_line: Option) -> Option { } None } + +#[allow(dead_code)] +fn is_blacklisted_lldb_version(version: &str) -> bool { + version == "350" +} diff --git a/src/errors.rs b/src/errors.rs index c3da891..29ca54f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -64,7 +64,11 @@ pub struct Error { } #[derive(PartialEq, Debug)] -enum WhichLine { ThisLine, FollowPrevious(usize), AdjustBackward(usize) } +enum WhichLine { + ThisLine, + FollowPrevious(usize), + AdjustBackward(usize), +} /// Looks for either "//~| KIND MESSAGE" or "//~^^... KIND MESSAGE" /// The former is a "follow" that inherits its target from the preceding line; @@ -91,25 +95,22 @@ pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec { let tag = match cfg { Some(rev) => format!("//[{}]~", rev), - None => format!("//~") + None => format!("//~"), }; rdr.lines() - .enumerate() - .filter_map(|(line_num, line)| { - parse_expected(last_nonfollow_error, - line_num + 1, - &line.unwrap(), - &tag) - .map(|(which, error)| { - match which { - FollowPrevious(_) => {} - _ => last_nonfollow_error = Some(error.line_num), - } - error - }) - }) - .collect() + .enumerate() + .filter_map(|(line_num, line)| { + parse_expected(last_nonfollow_error, line_num + 1, &line.unwrap(), &tag) + .map(|(which, error)| { + match which { + FollowPrevious(_) => {} + _ => last_nonfollow_error = Some(error.line_num), + } + error + }) + }) + .collect() } fn parse_expected(last_nonfollow_error: Option, @@ -117,7 +118,10 @@ fn parse_expected(last_nonfollow_error: Option, line: &str, tag: &str) -> Option<(WhichLine, Error)> { - let start = match line.find(tag) { Some(i) => i, None => return None }; + let start = match line.find(tag) { + Some(i) => i, + None => return None, + }; let (follow, adjusts) = if line[start + tag.len()..].chars().next().unwrap() == '|' { (true, 0) } else { @@ -125,26 +129,25 @@ fn parse_expected(last_nonfollow_error: Option, }; let kind_start = start + tag.len() + adjusts + (follow as usize); let (kind, msg); - match - line[kind_start..].split_whitespace() - .next() - .expect("Encountered unexpected empty comment") - .parse::() - { + match line[kind_start..] + .split_whitespace() + .next() + .expect("Encountered unexpected empty comment") + .parse::() { Ok(k) => { // If we find `//~ ERROR foo` or something like that: kind = Some(k); let letters = line[kind_start..].chars(); msg = letters.skip_while(|c| c.is_whitespace()) - .skip_while(|c| !c.is_whitespace()) - .collect::(); + .skip_while(|c| !c.is_whitespace()) + .collect::(); } Err(_) => { // Otherwise we found `//~ foo`: kind = None; let letters = line[kind_start..].chars(); msg = letters.skip_while(|c| c.is_whitespace()) - .collect::(); + .collect::(); } } let msg = msg.trim().to_owned(); @@ -155,15 +158,25 @@ fn parse_expected(last_nonfollow_error: Option, preceding //~^ line."); (FollowPrevious(line_num), line_num) } else { - let which = - if adjusts > 0 { AdjustBackward(adjusts) } else { ThisLine }; + let which = if adjusts > 0 { + AdjustBackward(adjusts) + } else { + ThisLine + }; let line_num = line_num - adjusts; (which, line_num) }; debug!("line={} tag={:?} which={:?} kind={:?} msg={:?}", - line_num, tag, which, kind, msg); - Some((which, Error { line_num: line_num, - kind: kind, - msg: msg, })) + line_num, + tag, + which, + kind, + msg); + Some((which, + Error { + line_num: line_num, + kind: kind, + msg: msg, + })) } diff --git a/src/header.rs b/src/header.rs index 5bd5cb7..899a366 100644 --- a/src/header.rs +++ b/src/header.rs @@ -32,24 +32,23 @@ impl EarlyProps { should_fail: false, }; - iter_header(testfile, None, &mut |ln| { + iter_header(testfile, + None, + &mut |ln| { props.ignore = - props.ignore || - parse_name_directive(ln, "ignore-test") || + props.ignore || parse_name_directive(ln, "ignore-test") || parse_name_directive(ln, &ignore_target(config)) || parse_name_directive(ln, &ignore_architecture(config)) || parse_name_directive(ln, &ignore_stage(config)) || parse_name_directive(ln, &ignore_env(config)) || - (config.mode == common::Pretty && - parse_name_directive(ln, "ignore-pretty")) || + (config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) || (config.target != config.host && parse_name_directive(ln, "ignore-cross-compile")) || ignore_gdb(config, ln) || - ignore_lldb(config, ln); + ignore_lldb(config, ln) || + ignore_llvm(config, ln); - props.should_fail = - props.should_fail || - parse_name_directive(ln, "should-fail"); + props.should_fail = props.should_fail || parse_name_directive(ln, "should-fail"); }); return props; @@ -61,11 +60,11 @@ impl EarlyProps { format!("ignore-{}", util::get_arch(&config.target)) } fn ignore_stage(config: &Config) -> String { - format!("ignore-{}", - config.stage_id.split('-').next().unwrap()) + format!("ignore-{}", config.stage_id.split('-').next().unwrap()) } fn ignore_env(config: &Config) -> String { - format!("ignore-{}", util::get_env(&config.target).unwrap_or("")) + format!("ignore-{}", + util::get_env(&config.target).unwrap_or("")) } fn ignore_gdb(config: &Config, line: &str) -> bool { if config.mode != common::DebugInfoGdb { @@ -79,13 +78,12 @@ impl EarlyProps { if let Some(ref actual_version) = config.gdb_version { if line.contains("min-gdb-version") { let min_version = line.trim() - .split(' ') - .last() - .expect("Malformed GDB version directive"); + .split(' ') + .last() + .expect("Malformed GDB version directive"); // Ignore if actual version is smaller the minimum required // version - gdb_version_to_int(actual_version) < - gdb_version_to_int(min_version) + gdb_version_to_int(actual_version) < gdb_version_to_int(min_version) } else { false } @@ -106,13 +104,30 @@ impl EarlyProps { if let Some(ref actual_version) = config.lldb_version { if line.contains("min-lldb-version") { let min_version = line.trim() - .split(' ') - .last() - .expect("Malformed lldb version directive"); + .split(' ') + .last() + .expect("Malformed lldb version directive"); // Ignore if actual version is smaller the minimum required // version - lldb_version_to_int(actual_version) < - lldb_version_to_int(min_version) + lldb_version_to_int(actual_version) < lldb_version_to_int(min_version) + } else { + false + } + } else { + false + } + } + + fn ignore_llvm(config: &Config, line: &str) -> bool { + if let Some(ref actual_version) = config.llvm_version { + if line.contains("min-llvm-version") { + let min_version = line.trim() + .split(' ') + .last() + .expect("Malformed llvm version directive"); + // Ignore if actual version is smaller the minimum required + // version + &actual_version[..] < min_version } else { false } @@ -126,7 +141,7 @@ impl EarlyProps { #[derive(Clone, Debug)] pub struct TestProps { // Lines that should be expected, in order, on standard out - pub error_patterns: Vec , + pub error_patterns: Vec, // Extra flags to pass to the compiler pub compile_flags: Vec, // Extra flags to pass when the compiled code is run (such as --bench) @@ -137,13 +152,13 @@ pub struct TestProps { // Other crates that should be compiled (typically from the same // directory as the test, but for backwards compatibility reasons // we also check the auxiliary directory) - pub aux_builds: Vec , + pub aux_builds: Vec, // Environment settings to use for compiling - pub rustc_env: Vec<(String,String)> , + pub rustc_env: Vec<(String, String)>, // Environment settings to use during execution - pub exec_env: Vec<(String,String)> , + pub exec_env: Vec<(String, String)>, // Lines to check if they appear in the expected debugger output - pub check_lines: Vec , + pub check_lines: Vec, // Build documentation for all specified aux-builds as well pub build_aux_docs: bool, // Flag to force a crate to be built with the host architecture @@ -226,17 +241,17 @@ impl TestProps { /// tied to a particular revision `foo` (indicated by writing /// `//[foo]`), then the property is ignored unless `cfg` is /// `Some("foo")`. - pub fn load_from(&mut self, testfile: &Path, cfg: Option<&str>) { - iter_header(testfile, cfg, &mut |ln| { + pub fn load_from(&mut self, testfile: &Path, cfg: Option<&str>) { + iter_header(testfile, + cfg, + &mut |ln| { if let Some(ep) = parse_error_pattern(ln) { self.error_patterns.push(ep); } if let Some(flags) = parse_compile_flags(ln) { - self.compile_flags.extend( - flags - .split_whitespace() - .map(|s| s.to_owned())); + self.compile_flags.extend(flags.split_whitespace() + .map(|s| s.to_owned())); } if let Some(r) = parse_revisions(ln) { @@ -279,7 +294,7 @@ impl TestProps { self.pretty_compare_only = parse_pretty_compare_only(ln); } - if let Some(ab) = parse_aux_build(ln) { + if let Some(ab) = parse_aux_build(ln) { self.aux_builds.push(ab); } @@ -291,7 +306,7 @@ impl TestProps { self.rustc_env.push(ee); } - if let Some(cl) = parse_check_line(ln) { + if let Some(cl) = parse_check_line(ln) { self.check_lines.push(cl); } @@ -302,21 +317,20 @@ impl TestProps { for key in vec!["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] { match env::var(key) { - Ok(val) => + Ok(val) => { if self.exec_env.iter().find(|&&(ref x, _)| *x == key).is_none() { self.exec_env.push((key.to_owned(), val)) - }, + } + } Err(..) => {} } } } } -fn iter_header(testfile: &Path, - cfg: Option<&str>, - it: &mut FnMut(&str)) { +fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) { if testfile.is_dir() { - return + return; } let rdr = BufReader::new(File::open(testfile).unwrap()); for ln in rdr.lines() { @@ -336,7 +350,7 @@ fn iter_header(testfile: &Path, None => false, }; if matches { - it(&ln[close_brace+1..]); + it(&ln[close_brace + 1..]); } } else { panic!("malformed condition directive: expected `//[foo]`, found `{}`", @@ -346,6 +360,7 @@ fn iter_header(testfile: &Path, it(&ln[2..]); } } + return; } fn parse_error_pattern(line: &str) -> Option { @@ -408,8 +423,7 @@ fn parse_pretty_compare_only(line: &str) -> bool { fn parse_env(line: &str, name: &str) -> Option<(String, String)> { parse_name_value_directive(line, name).map(|nv| { // nv is either FOO or FOO=BAR - let mut strs: Vec = nv - .splitn(2, '=') + let mut strs: Vec = nv.splitn(2, '=') .map(str::to_owned) .collect(); @@ -419,7 +433,7 @@ fn parse_env(line: &str, name: &str) -> Option<(String, String)> { let end = strs.pop().unwrap(); (strs.pop().unwrap(), end) } - n => panic!("Expected 1 or 2 strings, not {}", n) + n => panic!("Expected 1 or 2 strings, not {}", n), } }) } @@ -444,7 +458,7 @@ fn parse_name_directive(line: &str, directive: &str) -> bool { pub fn parse_name_value_directive(line: &str, directive: &str) -> Option { let keycolon = format!("{}:", directive); if let Some(colon) = line.find(&keycolon) { - let value = line[(colon + keycolon.len()) .. line.len()].to_owned(); + let value = line[(colon + keycolon.len())..line.len()].to_owned(); debug!("{}: {}", directive, value); Some(value) } else { @@ -453,9 +467,8 @@ pub fn parse_name_value_directive(line: &str, directive: &str) -> Option } pub fn gdb_version_to_int(version_string: &str) -> isize { - let error_string = format!( - "Encountered GDB version string with unexpected format: {}", - version_string); + let error_string = format!("Encountered GDB version string with unexpected format: {}", + version_string); let error_string = error_string; let components: Vec<&str> = version_string.trim().split('.').collect(); @@ -467,14 +480,13 @@ pub fn gdb_version_to_int(version_string: &str) -> isize { let major: isize = components[0].parse().ok().expect(&error_string); let minor: isize = components[1].parse().ok().expect(&error_string); - major * 1000 + minor + return major * 1000 + minor; } pub fn lldb_version_to_int(version_string: &str) -> isize { - let error_string = format!( - "Encountered LLDB version string with unexpected format: {}", - version_string); + let error_string = format!("Encountered LLDB version string with unexpected format: {}", + version_string); let error_string = error_string; - - version_string.parse().ok().expect(&error_string) + let major: isize = version_string.parse().ok().expect(&error_string); + return major; } diff --git a/src/json.rs b/src/json.rs index 84b7854..d9da1bd 100644 --- a/src/json.rs +++ b/src/json.rs @@ -12,10 +12,10 @@ use errors::{Error, ErrorKind}; use rustc_serialize::json; use std::str::FromStr; use std::path::Path; -use runtest::{ProcRes}; +use runtest::ProcRes; // These structs are a subset of the ones found in -// `syntax::errors::json`. +// `syntax::json`. #[derive(RustcEncodable, RustcDecodable)] struct Diagnostic { @@ -58,8 +58,8 @@ struct DiagnosticCode { pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec { output.lines() - .flat_map(|line| parse_line(file_name, line, output, proc_res)) - .collect() + .flat_map(|line| parse_line(file_name, line, output, proc_res)) + .collect() } fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) -> Vec { @@ -73,9 +73,11 @@ fn parse_line(file_name: &str, line: &str, output: &str, proc_res: &ProcRes) -> expected_errors } Err(error) => { - proc_res.fatal(Some(&format!( - "failed to decode compiler output as json: `{}`\noutput: {}\nline: {}", - error, line, output))); + proc_res.fatal(Some(&format!("failed to decode compiler output as json: \ + `{}`\noutput: {}\nline: {}", + error, + line, + output))); } } } else { @@ -87,16 +89,15 @@ fn push_expected_errors(expected_errors: &mut Vec, diagnostic: &Diagnostic, default_spans: &[&DiagnosticSpan], file_name: &str) { - let spans_in_this_file: Vec<_> = - diagnostic.spans.iter() - .filter(|span| Path::new(&span.file_name) == Path::new(&file_name)) - .collect(); - - let primary_spans: Vec<_> = - spans_in_this_file.iter() - .cloned() - .filter(|span| span.is_primary) - .collect(); + let spans_in_this_file: Vec<_> = diagnostic.spans + .iter() + .filter(|span| Path::new(&span.file_name) == Path::new(&file_name)) + .collect(); + + let primary_spans: Vec<_> = spans_in_this_file.iter() + .cloned() + .filter(|span| span.is_primary) + .collect(); let primary_spans = if primary_spans.is_empty() { // subdiagnostics often don't have a span of their own; // inherit the span from the parent in that case @@ -144,24 +145,20 @@ fn push_expected_errors(expected_errors: &mut Vec, for span in primary_spans { let msg = with_code(span, first_line); let kind = ErrorKind::from_str(&diagnostic.level).ok(); - expected_errors.push( - Error { - line_num: span.line_start, - kind: kind, - msg: msg, - } - ); + expected_errors.push(Error { + line_num: span.line_start, + kind: kind, + msg: msg, + }); } } for next_line in message_lines { for span in primary_spans { - expected_errors.push( - Error { - line_num: span.line_start, - kind: None, - msg: with_code(span, next_line), - } - ); + expected_errors.push(Error { + line_num: span.line_start, + kind: None, + msg: with_code(span, next_line), + }); } } @@ -170,33 +167,28 @@ fn push_expected_errors(expected_errors: &mut Vec, let start_line = primary_spans.iter().map(|s| s.line_start).min().expect("\ every suggestion should have at least one span"); for (index, line) in rendered.lines().enumerate() { - expected_errors.push( - Error { - line_num: start_line + index, - kind: Some(ErrorKind::Suggestion), - msg: line.to_string() - } - ); + expected_errors.push(Error { + line_num: start_line + index, + kind: Some(ErrorKind::Suggestion), + msg: line.to_string(), + }); } } // Add notes for the backtrace for span in primary_spans { for frame in &span.expansion { - push_backtrace(expected_errors, - frame, - file_name); + push_backtrace(expected_errors, frame, file_name); } } // Add notes for any labels that appear in the message. for span in spans_in_this_file.iter() - .filter(|span| span.label.is_some()) - { + .filter(|span| span.label.is_some()) { expected_errors.push(Error { line_num: span.line_start, kind: Some(ErrorKind::Note), - msg: span.label.clone().unwrap() + msg: span.label.clone().unwrap(), }); } @@ -210,13 +202,11 @@ fn push_backtrace(expected_errors: &mut Vec, expansion: &DiagnosticSpanMacroExpansion, file_name: &str) { if Path::new(&expansion.span.file_name) == Path::new(&file_name) { - expected_errors.push( - Error { - line_num: expansion.span.line_start, - kind: Some(ErrorKind::Note), - msg: format!("in this expansion of {}", expansion.macro_decl_name), - } - ); + expected_errors.push(Error { + line_num: expansion.span.line_start, + kind: Some(ErrorKind::Note), + msg: format!("in this expansion of {}", expansion.macro_decl_name), + }); } for previous_expansion in &expansion.span.expansion { diff --git a/src/procsrv.rs b/src/procsrv.rs index b1868a1..ed690c0 100644 --- a/src/procsrv.rs +++ b/src/procsrv.rs @@ -12,7 +12,7 @@ use std::env; use std::ffi::OsString; use std::io::prelude::*; use std::path::PathBuf; -use std::process::{ExitStatus, Command, Child, Output, Stdio}; +use std::process::{Child, Command, ExitStatus, Output, Stdio}; pub fn dylib_env_var() -> &'static str { if cfg!(windows) { @@ -29,9 +29,9 @@ fn add_target_env(cmd: &mut Command, lib_path: &str, aux_path: Option<&str>) { // search path for the child. let var = dylib_env_var(); let mut path = env::split_paths(&env::var_os(var).unwrap_or(OsString::new())) - .collect::>(); + .collect::>(); if let Some(p) = aux_path { - path.insert(0, PathBuf::from(p)); + path.insert(0, PathBuf::from(p)) } path.insert(0, PathBuf::from(lib_path)); @@ -43,21 +43,22 @@ fn add_target_env(cmd: &mut Command, lib_path: &str, aux_path: Option<&str>) { pub struct Result { pub status: ExitStatus, pub out: String, - pub err: String + pub err: String, } pub fn run(lib_path: &str, prog: &str, aux_path: Option<&str>, args: &[String], - env: Vec<(String, String)> , - input: Option) -> Option { + env: Vec<(String, String)>, + input: Option) + -> Option { let mut cmd = Command::new(prog); cmd.args(args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); add_target_env(&mut cmd, lib_path, aux_path); for (key, val) in env { cmd.env(&key, &val); @@ -68,31 +69,31 @@ pub fn run(lib_path: &str, if let Some(input) = input { process.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap(); } - let Output { status, stdout, stderr } = - process.wait_with_output().unwrap(); + let Output { status, stdout, stderr } = process.wait_with_output().unwrap(); Some(Result { status: status, out: String::from_utf8(stdout).unwrap(), - err: String::from_utf8(stderr).unwrap() + err: String::from_utf8(stderr).unwrap(), }) - }, - Err(..) => None + } + Err(..) => None, } } pub fn run_background(lib_path: &str, - prog: &str, - aux_path: Option<&str>, - args: &[String], - env: Vec<(String, String)> , - input: Option) -> Option { + prog: &str, + aux_path: Option<&str>, + args: &[String], + env: Vec<(String, String)>, + input: Option) + -> Option { let mut cmd = Command::new(prog); cmd.args(args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); add_target_env(&mut cmd, lib_path, aux_path); for (key, val) in env { cmd.env(&key, &val); @@ -105,7 +106,7 @@ pub fn run_background(lib_path: &str, } Some(process) - }, - Err(..) => None + } + Err(..) => None, } } diff --git a/src/runtest.rs b/src/runtest.rs index 42edbd4..905791c 100644 --- a/src/runtest.rs +++ b/src/runtest.rs @@ -11,7 +11,7 @@ use common::Config; use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits}; -use common::{Incremental, RunMake, Ui}; +use common::{Incremental, RunMake, Ui, MirOpt}; use errors::{self, ErrorKind, Error}; use json; use header::TestProps; @@ -117,6 +117,7 @@ impl<'test> TestCx<'test> { Incremental => self.run_incremental_test(), RunMake => self.run_rmake_test(), Ui => self.run_ui_test(), + MirOpt => self.run_mir_opt_test(), } } @@ -136,10 +137,6 @@ impl<'test> TestCx<'test> { self.check_correct_failure_status(&proc_res); - if proc_res.status.success() { - self.fatal("process did not return an error status"); - } - let output_to_check = self.get_output(&proc_res); let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); if !expected_errors.is_empty() { @@ -200,6 +197,11 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec("compilation failed!", &proc_res); } + let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); + if !expected_errors.is_empty() { + self.check_expected_errors(expected_errors, &proc_res); + } + let proc_res = self.exec_compiled_test(); if !proc_res.status.success() { @@ -976,7 +978,7 @@ actual:\n\ fn check_no_compiler_crash(&self, proc_res: &ProcRes) { for line in proc_res.stderr.lines() { - if line.starts_with("error: internal compiler error:") { + if line.contains("error: internal compiler error") { self.fatal_proc_rec("compiler encountered internal error", proc_res); } } @@ -995,7 +997,8 @@ actual:\n\ fn check_expected_errors(&self, expected_errors: Vec, proc_res: &ProcRes) { - if proc_res.status.success() { + if proc_res.status.success() && + expected_errors.iter().any(|x| x.kind == Some(ErrorKind::Error)) { self.fatal_proc_rec("process did not return an error status", proc_res); } @@ -1012,8 +1015,7 @@ actual:\n\ // Parse the JSON output from the compiler and extract out the messages. let actual_errors = json::parse_output(&file_name, &proc_res.stderr, &proc_res); - let mut unexpected = 0; - let mut not_found = 0; + let mut unexpected = Vec::new(); let mut found = vec![false; expected_errors.len()]; for actual_error in &actual_errors { let opt_index = @@ -1045,12 +1047,13 @@ actual:\n\ .map_or(String::from("message"), |k| k.to_string()), actual_error.msg)); - unexpected += 1; + unexpected.push(actual_error.clone()); } } } } + let mut not_found = Vec::new(); // anything not yet found is a problem for (index, expected_error) in expected_errors.iter().enumerate() { if !found[index] { @@ -1062,18 +1065,22 @@ actual:\n\ .map_or("message".into(), |k| k.to_string()), expected_error.msg)); - not_found += 1; + not_found.push(expected_error.clone()); } } - if unexpected > 0 || not_found > 0 { + if unexpected.len() > 0 || not_found.len() > 0 { self.error( &format!("{} unexpected errors found, {} expected errors not found", - unexpected, not_found)); + unexpected.len(), not_found.len())); print!("status: {}\ncommand: {}\n", proc_res.status, proc_res.cmdline); - println!("actual errors (from JSON output): {:#?}\n", actual_errors); - println!("expected errors (from test file): {:#?}\n", expected_errors); + if unexpected.len() > 0 { + println!("unexpected errors (from JSON output): {:#?}\n", unexpected); + } + if not_found.len() > 0 { + println!("not found errors (from test file): {:#?}\n", not_found); + } panic!(); } } @@ -1319,22 +1326,37 @@ actual:\n\ match self.config.mode { CompileFail | ParseFail | + RunPass | Incremental => { // If we are extracting and matching errors in the new // fashion, then you want JSON mode. Old-skool error // patterns still match the raw compiler output. if self.props.error_patterns.is_empty() { args.extend(["--error-format", - "json", - "-Z", - "unstable-options"] + "json"] .iter() .map(|s| s.to_string())); } } - + MirOpt => { + args.extend(["-Z", + "dump-mir=all", + "-Z", + "mir-opt-level=3", + "-Z"] + .iter() + .map(|s| s.to_string())); + + + let mir_dump_dir = self.get_mir_dump_dir(); + self.create_dir_racy(mir_dump_dir.as_path()); + let mut dir_opt = "dump-mir-dir=".to_string(); + dir_opt.push_str(mir_dump_dir.to_str().unwrap()); + debug!("dir_opt: {:?}", dir_opt); + + args.push(dir_opt); + } RunFail | - RunPass | RunPassValgrind | Pretty | DebugInfoGdb | @@ -1522,7 +1544,7 @@ actual:\n\ /// Given a test path like `compile-fail/foo/bar.rs` Returns a name like /// - /// ```rust,ignore + /// ```ignore /// /foo/bar-stage1 /// ``` fn output_base_name(&self) -> PathBuf { @@ -1956,7 +1978,10 @@ actual:\n\ // runs. let incremental_dir = self.incremental_dir(); if incremental_dir.exists() { - fs::remove_dir_all(&incremental_dir).unwrap(); + // Canonicalizing the path will convert it to the //?/ format + // on Windows, which enables paths longer than 260 character + let canonicalized = incremental_dir.canonicalize().unwrap(); + fs::remove_dir_all(canonicalized).unwrap(); } fs::create_dir_all(&incremental_dir).unwrap(); @@ -1994,6 +2019,7 @@ actual:\n\ // Add an extra flag pointing at the incremental directory. let mut revision_props = self.props.clone(); revision_props.incremental_dir = Some(incremental_dir); + revision_props.compile_flags.push(String::from("-Zincremental-info")); let revision_cx = TestCx { config: self.config, @@ -2020,7 +2046,7 @@ actual:\n\ /// Directory where incremental work products are stored. fn incremental_dir(&self) -> PathBuf { - self.output_base_name().with_extension("incremental") + self.output_base_name().with_extension("inc") } fn run_rmake_test(&self) { @@ -2105,7 +2131,7 @@ actual:\n\ } else { Err(e) } - })) + })); } } fs::remove_dir(path) @@ -2143,6 +2169,100 @@ actual:\n\ } } + fn run_mir_opt_test(&self) { + let proc_res = self.compile_test(); + + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + + let proc_res = self.exec_compiled_test(); + + if !proc_res.status.success() { + self.fatal_proc_rec("test run failed!", &proc_res); + } + self.check_mir_dump(); + } + + fn check_mir_dump(&self) { + let mut test_file_contents = String::new(); + fs::File::open(self.testpaths.file.clone()).unwrap() + .read_to_string(&mut test_file_contents) + .unwrap(); + if let Some(idx) = test_file_contents.find("// END RUST SOURCE") { + let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len()); + let tests_text_str = String::from(tests_text); + let mut curr_test : Option<&str> = None; + let mut curr_test_contents = Vec::new(); + for l in tests_text_str.lines() { + debug!("line: {:?}", l); + if l.starts_with("// START ") { + let (_, t) = l.split_at("// START ".len()); + curr_test = Some(t); + } else if l.starts_with("// END") { + let (_, t) = l.split_at("// END ".len()); + if Some(t) != curr_test { + panic!("mismatched START END test name"); + } + self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents); + curr_test = None; + curr_test_contents.clear(); + } else if l.is_empty() { + // ignore + } else if l.starts_with("// ") { + let (_, test_content) = l.split_at("// ".len()); + curr_test_contents.push(test_content); + } + } + } + } + + fn compare_mir_test_output(&self, test_name: &str, expected_content: &Vec<&str>) { + let mut output_file = PathBuf::new(); + output_file.push(self.get_mir_dump_dir()); + output_file.push(test_name); + debug!("comparing the contests of: {:?}", output_file); + debug!("with: {:?}", expected_content); + + let mut dumped_file = fs::File::open(output_file.clone()).unwrap(); + let mut dumped_string = String::new(); + dumped_file.read_to_string(&mut dumped_string).unwrap(); + let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty()); + let mut expected_lines = expected_content.iter().filter(|l| !l.is_empty()); + + // We expect each non-empty line from expected_content to appear + // in the dump in order, but there may be extra lines interleaved + while let Some(expected_line) = expected_lines.next() { + let e_norm = normalize_mir_line(expected_line); + if e_norm.is_empty() { + continue; + }; + let mut found = false; + while let Some(dumped_line) = dumped_lines.next() { + let d_norm = normalize_mir_line(dumped_line); + debug!("found: {:?}", d_norm); + debug!("expected: {:?}", e_norm); + if e_norm == d_norm { + found = true; + break; + }; + } + if !found { + panic!("ran out of mir dump output to match against"); + } + } + } + + fn get_mir_dump_dir(&self) -> PathBuf { + let mut mir_dump_dir = PathBuf::from(self.config.build_base + .as_path() + .to_str() + .unwrap()); + debug!("input_file: {:?}", self.testpaths.file); + mir_dump_dir.push(self.testpaths.file.file_stem().unwrap().to_str().unwrap()); + mir_dump_dir + } + fn normalize_output(&self, output: &str) -> String { let parent_dir = self.testpaths.file.parent().unwrap(); let parent_dir_str = parent_dir.display().to_string(); @@ -2272,3 +2392,12 @@ enum TargetLocation { ThisDirectory(PathBuf), } +fn normalize_mir_line(line: &str) -> String { + let no_comments = if let Some(idx) = line.find("//") { + let (l, _) = line.split_at(idx); + l + } else { + line + }; + no_comments.replace(char::is_whitespace, "") +} diff --git a/src/uidiff.rs b/src/uidiff.rs index 6657339..fca0102 100644 --- a/src/uidiff.rs +++ b/src/uidiff.rs @@ -13,24 +13,23 @@ pub fn diff_lines(actual: &str, expected: &str) -> Vec { // mega simplistic diff algorithm that just prints the things added/removed - zip_all(actual.lines(), expected.lines()).enumerate().filter_map(|(i, (a,e))| { - match (a, e) { - (Some(a), Some(e)) => { - if lines_match(e, a) { - None - } else { - Some(format!("{:3} - |{}|\n + |{}|\n", i, e, a)) + zip_all(actual.lines(), expected.lines()) + .enumerate() + .filter_map(|(i, (a, e))| { + match (a, e) { + (Some(a), Some(e)) => { + if lines_match(e, a) { + None + } else { + Some(format!("{:3} - |{}|\n + |{}|\n", i, e, a)) + } } - }, - (Some(a), None) => { - Some(format!("{:3} -\n + |{}|\n", i, a)) - }, - (None, Some(e)) => { - Some(format!("{:3} - |{}|\n +\n", i, e)) - }, - (None, None) => panic!("Cannot get here") - } - }).collect() + (Some(a), None) => Some(format!("{:3} -\n + |{}|\n", i, a)), + (None, Some(e)) => Some(format!("{:3} - |{}|\n +\n", i, e)), + (None, None) => panic!("Cannot get here"), + } + }) + .collect() } fn lines_match(expected: &str, mut actual: &str) -> bool { @@ -38,13 +37,11 @@ fn lines_match(expected: &str, mut actual: &str) -> bool { match actual.find(part) { Some(j) => { if i == 0 && j != 0 { - return false + return false; } actual = &actual[j + part.len()..]; } - None => { - return false - } + None => return false, } } actual.is_empty() || expected.ends_with("[..]") @@ -55,7 +52,7 @@ struct ZipAll { second: I2, } -impl, I2: Iterator> Iterator for ZipAll { +impl, I2: Iterator> Iterator for ZipAll { type Item = (Option, Option); fn next(&mut self) -> Option<(Option, Option)> { let first = self.first.next(); @@ -63,12 +60,12 @@ impl, I2: Iterator> Iterator for ZipAll match (first, second) { (None, None) => None, - (a, b) => Some((a, b)) + (a, b) => Some((a, b)), } } } -fn zip_all, I2: Iterator>(a: I1, b: I2) -> ZipAll { +fn zip_all, I2: Iterator>(a: I1, b: I2) -> ZipAll { ZipAll { first: a, second: b, diff --git a/src/util.rs b/src/util.rs index 69b839c..2db5394 100644 --- a/src/util.rs +++ b/src/util.rs @@ -12,46 +12,42 @@ use std::env; use common::Config; /// Conversion table from triple OS name to Rust SYSNAME -const OS_TABLE: &'static [(&'static str, &'static str)] = &[ - ("android", "android"), - ("bitrig", "bitrig"), - ("darwin", "macos"), - ("dragonfly", "dragonfly"), - ("freebsd", "freebsd"), - ("ios", "ios"), - ("linux", "linux"), - ("mingw32", "windows"), - ("netbsd", "netbsd"), - ("openbsd", "openbsd"), - ("win32", "windows"), - ("windows", "windows"), - ("solaris", "solaris"), - ("emscripten", "emscripten"), -]; +const OS_TABLE: &'static [(&'static str, &'static str)] = &[("android", "android"), + ("bitrig", "bitrig"), + ("darwin", "macos"), + ("dragonfly", "dragonfly"), + ("freebsd", "freebsd"), + ("ios", "ios"), + ("linux", "linux"), + ("mingw32", "windows"), + ("netbsd", "netbsd"), + ("openbsd", "openbsd"), + ("win32", "windows"), + ("windows", "windows"), + ("solaris", "solaris"), + ("emscripten", "emscripten")]; -const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ - ("aarch64", "aarch64"), - ("amd64", "x86_64"), - ("arm", "arm"), - ("arm64", "aarch64"), - ("hexagon", "hexagon"), - ("i386", "x86"), - ("i686", "x86"), - ("mips", "mips"), - ("msp430", "msp430"), - ("powerpc", "powerpc"), - ("powerpc64", "powerpc64"), - ("s390x", "systemz"), - ("sparc", "sparc"), - ("x86_64", "x86_64"), - ("xcore", "xcore"), - ("asmjs", "asmjs"), -]; +const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[("aarch64", "aarch64"), + ("amd64", "x86_64"), + ("arm", "arm"), + ("arm64", "aarch64"), + ("hexagon", "hexagon"), + ("i386", "x86"), + ("i686", "x86"), + ("mips", "mips"), + ("msp430", "msp430"), + ("powerpc", "powerpc"), + ("powerpc64", "powerpc64"), + ("s390x", "s390x"), + ("sparc", "sparc"), + ("x86_64", "x86_64"), + ("xcore", "xcore"), + ("asmjs", "asmjs")]; pub fn get_os(triple: &str) -> &'static str { for &(triple_os, os) in OS_TABLE { if triple.contains(triple_os) { - return os + return os; } } panic!("Cannot determine OS from triple"); @@ -59,7 +55,7 @@ pub fn get_os(triple: &str) -> &'static str { pub fn get_arch(triple: &str) -> &'static str { for &(triple_arch, arch) in ARCH_TABLE { if triple.contains(triple_arch) { - return arch + return arch; } } panic!("Cannot determine Architecture from triple"); @@ -74,17 +70,21 @@ pub fn make_new_path(path: &str) -> String { // Windows just uses PATH as the library search path, so we have to // maintain the current value while adding our own match env::var(lib_path_env_var()) { - Ok(curr) => { - format!("{}{}{}", path, path_div(), curr) - } - Err(..) => path.to_owned() + Ok(curr) => format!("{}{}{}", path, path_div(), curr), + Err(..) => path.to_owned(), } } -pub fn lib_path_env_var() -> &'static str { "PATH" } -fn path_div() -> &'static str { ";" } +pub fn lib_path_env_var() -> &'static str { + "PATH" +} +fn path_div() -> &'static str { + ";" +} pub fn logv(config: &Config, s: String) { debug!("{}", s); - if config.verbose { println!("{}", s); } + if config.verbose { + println!("{}", s); + } }