summaryrefslogtreecommitdiffstats
path: root/vendor/compiletest_rs/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/compiletest_rs/src')
-rw-r--r--vendor/compiletest_rs/src/common.rs425
-rw-r--r--vendor/compiletest_rs/src/errors.rs182
-rw-r--r--vendor/compiletest_rs/src/header.rs709
-rw-r--r--vendor/compiletest_rs/src/json.rs263
-rw-r--r--vendor/compiletest_rs/src/lib.rs458
-rw-r--r--vendor/compiletest_rs/src/raise_fd_limit.rs69
-rw-r--r--vendor/compiletest_rs/src/read2.rs208
-rw-r--r--vendor/compiletest_rs/src/runtest.rs2894
-rw-r--r--vendor/compiletest_rs/src/uidiff.rs73
-rw-r--r--vendor/compiletest_rs/src/util.rs134
10 files changed, 5415 insertions, 0 deletions
diff --git a/vendor/compiletest_rs/src/common.rs b/vendor/compiletest_rs/src/common.rs
new file mode 100644
index 000000000..17eaa5447
--- /dev/null
+++ b/vendor/compiletest_rs/src/common.rs
@@ -0,0 +1,425 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+pub use self::Mode::*;
+
+use std::env;
+use std::fmt;
+use std::fs::{read_dir, remove_file};
+use std::str::FromStr;
+use std::path::PathBuf;
+#[cfg(feature = "rustc")]
+use rustc_session;
+
+use test::ColorConfig;
+use runtest::dylib_env_var;
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+pub enum Mode {
+ CompileFail,
+ ParseFail,
+ RunFail,
+ RunPass,
+ RunPassValgrind,
+ Pretty,
+ DebugInfoGdb,
+ DebugInfoLldb,
+ Codegen,
+ Rustdoc,
+ CodegenUnits,
+ Incremental,
+ RunMake,
+ Ui,
+ MirOpt,
+ Assembly,
+}
+
+impl Mode {
+ pub fn disambiguator(self) -> &'static str {
+ // Run-pass and pretty run-pass tests could run concurrently, and if they do,
+ // they need to keep their output segregated. Same is true for debuginfo tests that
+ // can be run both on gdb and lldb.
+ match self {
+ Pretty => ".pretty",
+ DebugInfoGdb => ".gdb",
+ DebugInfoLldb => ".lldb",
+ _ => "",
+ }
+ }
+}
+
+impl FromStr for Mode {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Mode, ()> {
+ 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),
+ "mir-opt" => Ok(MirOpt),
+ "assembly" => Ok(Assembly),
+ _ => Err(()),
+ }
+ }
+}
+
+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",
+ MirOpt => "mir-opt",
+ Assembly => "assembly",
+ },
+ f)
+ }
+}
+
+#[derive(Clone)]
+pub struct Config {
+ /// `true` to overwrite stderr/stdout/fixed files instead of complaining about changes in output.
+ pub bless: bool,
+
+ /// The library paths required for running the compiler
+ pub compile_lib_path: PathBuf,
+
+ /// The library paths required for running compiled programs
+ pub run_lib_path: PathBuf,
+
+ /// The rustc executable
+ pub rustc_path: PathBuf,
+
+ /// The rustdoc executable
+ pub rustdoc_path: Option<PathBuf>,
+
+ /// The python executable to use for LLDB
+ pub lldb_python: String,
+
+ /// The python executable to use for htmldocck
+ pub docck_python: String,
+
+ /// The llvm FileCheck binary path
+ pub llvm_filecheck: Option<PathBuf>,
+
+ /// The valgrind path
+ pub valgrind_path: Option<String>,
+
+ /// Whether to fail if we can't run run-pass-valgrind tests under valgrind
+ /// (or, alternatively, to silently run them like regular run-pass tests).
+ pub force_valgrind: bool,
+
+ /// The directory containing the tests to run
+ pub src_base: PathBuf,
+
+ /// The directory where programs should be built
+ pub build_base: PathBuf,
+
+ /// The name of the stage being built (stage1, etc)
+ pub stage_id: String,
+
+ /// The test mode, compile-fail, run-fail, run-pass
+ pub mode: Mode,
+
+ /// Run ignored tests
+ pub run_ignored: bool,
+
+ /// Only run tests that match these filters
+ pub filters: Vec<String>,
+
+ /// Exactly match the filter, rather than a substring
+ pub filter_exact: bool,
+
+ /// Write out a parseable log of tests that were run
+ pub logfile: Option<PathBuf>,
+
+ /// A command line to prefix program execution with,
+ /// for running under valgrind
+ pub runtool: Option<String>,
+
+ /// Flags to pass to the compiler when building for the host
+ pub host_rustcflags: Option<String>,
+
+ /// Flags to pass to the compiler when building for the target
+ pub target_rustcflags: Option<String>,
+
+ /// Target system to be tested
+ pub target: String,
+
+ /// Host triple for the compiler being invoked
+ pub host: String,
+
+ /// Path to / name of the GDB executable
+ pub gdb: Option<String>,
+
+ /// Version of GDB, encoded as ((major * 1000) + minor) * 1000 + patch
+ pub gdb_version: Option<u32>,
+
+ /// Whether GDB has native rust support
+ pub gdb_native_rust: bool,
+
+ /// Version of LLDB
+ pub lldb_version: Option<String>,
+
+ /// Version of LLVM
+ pub llvm_version: Option<String>,
+
+ /// Is LLVM a system LLVM
+ pub system_llvm: bool,
+
+ /// Path to the android tools
+ pub android_cross_path: PathBuf,
+
+ /// Extra parameter to run adb on arm-linux-androideabi
+ pub adb_path: String,
+
+ /// Extra parameter to run test suite on arm-linux-androideabi
+ pub adb_test_dir: String,
+
+ /// status whether android device available or not
+ pub adb_device_status: bool,
+
+ /// the path containing LLDB's Python module
+ pub lldb_python_dir: Option<String>,
+
+ /// Explain what's going on
+ pub verbose: bool,
+
+ /// Print one character per test instead of one line
+ pub quiet: bool,
+
+ /// Whether to use colors in test.
+ pub color: ColorConfig,
+
+ /// where to find the remote test client process, if we're using it
+ pub remote_test_client: Option<PathBuf>,
+
+ /// If true, this will generate a coverage file with UI test files that run `MachineApplicable`
+ /// diagnostics but are missing `run-rustfix` annotations. The generated coverage file is
+ /// created in `/<build_base>/rustfix_missing_coverage.txt`
+ pub rustfix_coverage: bool,
+
+ /// The default Rust edition
+ pub edition: Option<String>,
+
+ // Configuration for various run-make tests frobbing things like C compilers
+ // or querying about various LLVM component information.
+ pub cc: String,
+ pub cxx: String,
+ pub cflags: String,
+ pub ar: String,
+ pub linker: Option<String>,
+ pub llvm_components: String,
+ pub llvm_cxxflags: String,
+ pub nodejs: Option<String>,
+}
+
+#[derive(Clone)]
+pub struct TestPaths {
+ pub file: PathBuf, // e.g., compile-test/foo/bar/baz.rs
+ pub base: PathBuf, // e.g., compile-test, auxiliary
+ pub relative_dir: PathBuf, // e.g., foo/bar
+}
+
+/// Used by `ui` tests to generate things like `foo.stderr` from `foo.rs`.
+pub fn expected_output_path(
+ testpaths: &TestPaths,
+ revision: Option<&str>,
+ kind: &str,
+) -> PathBuf {
+ assert!(UI_EXTENSIONS.contains(&kind));
+ let mut parts = Vec::new();
+
+ if let Some(x) = revision {
+ parts.push(x);
+ }
+ parts.push(kind);
+
+ let extension = parts.join(".");
+ testpaths.file.with_extension(extension)
+}
+
+pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED];
+pub const UI_STDERR: &str = "stderr";
+pub const UI_STDOUT: &str = "stdout";
+pub const UI_FIXED: &str = "fixed";
+
+impl Config {
+ /// Add rustc flags to link with the crate's dependencies in addition to the crate itself
+ pub fn link_deps(&mut self) {
+ let varname = dylib_env_var();
+
+ // Dependencies can be found in the environment variable. Throw everything there into the
+ // link flags
+ let lib_paths = env::var(varname).unwrap_or_else(|err| match err {
+ env::VarError::NotPresent => String::new(),
+ err => panic!("can't get {} environment variable: {}", varname, err),
+ });
+
+ // Append to current flags if any are set, otherwise make new String
+ let mut flags = self.target_rustcflags.take().unwrap_or_else(String::new);
+ if !lib_paths.is_empty() {
+ for p in env::split_paths(&lib_paths) {
+ flags += " -L ";
+ flags += p.to_str().unwrap(); // Can't fail. We already know this is unicode
+ }
+ }
+
+ self.target_rustcflags = Some(flags);
+ }
+
+ /// Remove rmeta files from target `deps` directory
+ ///
+ /// These files are created by `cargo check`, and conflict with
+ /// `cargo build` rlib files, causing E0464 for tests which use
+ /// the parent crate.
+ pub fn clean_rmeta(&self) {
+ if self.target_rustcflags.is_some() {
+ for directory in self.target_rustcflags
+ .as_ref()
+ .unwrap()
+ .split_whitespace()
+ .filter(|s| s.ends_with("/deps"))
+ {
+ if let Ok(mut entries) = read_dir(directory) {
+ while let Some(Ok(entry)) = entries.next() {
+ if entry.file_name().to_string_lossy().ends_with(".rmeta") {
+ let _ = remove_file(entry.path());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #[cfg(feature = "tmp")]
+ pub fn tempdir(mut self) -> ConfigWithTemp {
+ let tmp = tempfile::Builder::new().prefix("compiletest").tempdir()
+ .expect("failed to create temporary directory");
+ self.build_base = tmp.path().to_owned();
+ config_tempdir::ConfigWithTemp {
+ config: self,
+ tempdir: tmp,
+ }
+ }
+}
+
+#[cfg(feature = "tmp")]
+mod config_tempdir {
+ use tempfile;
+ use std::ops;
+
+ pub struct ConfigWithTemp {
+ pub config: super::Config,
+ pub tempdir: tempfile::TempDir,
+ }
+
+ impl ops::Deref for ConfigWithTemp {
+ type Target = super::Config;
+
+ fn deref(&self) -> &Self::Target {
+ &self.config
+ }
+ }
+
+ impl ops::DerefMut for ConfigWithTemp {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.config
+ }
+ }
+}
+
+#[cfg(feature = "tmp")]
+pub use self::config_tempdir::ConfigWithTemp;
+
+
+impl Default for Config {
+ fn default() -> Config {
+ #[cfg(feature = "rustc")]
+ let platform = rustc_session::config::host_triple().to_string();
+
+ Config {
+ bless: false,
+ compile_lib_path: PathBuf::from(""),
+ run_lib_path: PathBuf::from(""),
+ rustc_path: PathBuf::from("rustc"),
+ rustdoc_path: None,
+ lldb_python: "python".to_owned(),
+ docck_python: "docck-python".to_owned(),
+ valgrind_path: None,
+ force_valgrind: false,
+ llvm_filecheck: None,
+ src_base: PathBuf::from("tests/run-pass"),
+ build_base: env::temp_dir(),
+ stage_id: "stage-id".to_owned(),
+ mode: Mode::RunPass,
+ run_ignored: false,
+ filters: vec![],
+ filter_exact: false,
+ logfile: None,
+ runtool: None,
+ host_rustcflags: None,
+ target_rustcflags: None,
+ #[cfg(feature = "rustc")]
+ target: platform.clone(),
+ #[cfg(not(feature = "rustc"))]
+ target: env!("TARGET").to_string(),
+ #[cfg(feature = "rustc")]
+ host: platform.clone(),
+ #[cfg(not(feature = "rustc"))]
+ host: env!("HOST").to_string(),
+ rustfix_coverage: false,
+ gdb: None,
+ gdb_version: None,
+ gdb_native_rust: false,
+ lldb_version: None,
+ llvm_version: None,
+ system_llvm: false,
+ android_cross_path: PathBuf::from("android-cross-path"),
+ adb_path: "adb-path".to_owned(),
+ adb_test_dir: "adb-test-dir/target".to_owned(),
+ adb_device_status: false,
+ lldb_python_dir: None,
+ verbose: false,
+ quiet: false,
+ color: ColorConfig::AutoColor,
+ remote_test_client: None,
+ cc: "cc".to_string(),
+ cxx: "cxx".to_string(),
+ cflags: "cflags".to_string(),
+ ar: "ar".to_string(),
+ linker: None,
+ llvm_components: "llvm-components".to_string(),
+ llvm_cxxflags: "llvm-cxxflags".to_string(),
+ nodejs: None,
+ edition: None,
+ }
+ }
+}
diff --git a/vendor/compiletest_rs/src/errors.rs b/vendor/compiletest_rs/src/errors.rs
new file mode 100644
index 000000000..251dd4d5e
--- /dev/null
+++ b/vendor/compiletest_rs/src/errors.rs
@@ -0,0 +1,182 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+use self::WhichLine::*;
+
+use std::fmt;
+use std::fs::File;
+use std::io::BufReader;
+use std::io::prelude::*;
+use std::path::Path;
+use std::str::FromStr;
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum ErrorKind {
+ Help,
+ Error,
+ Note,
+ Suggestion,
+ Warning,
+}
+
+impl FromStr for ErrorKind {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let s = s.to_uppercase();
+ let part0: &str = s.split(':').next().unwrap();
+ match part0 {
+ "HELP" => Ok(ErrorKind::Help),
+ "ERROR" => Ok(ErrorKind::Error),
+ "NOTE" => Ok(ErrorKind::Note),
+ "SUGGESTION" => Ok(ErrorKind::Suggestion),
+ "WARN" |
+ "WARNING" => Ok(ErrorKind::Warning),
+ _ => Err(()),
+ }
+ }
+}
+
+impl fmt::Display for ErrorKind {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ ErrorKind::Help => write!(f, "help message"),
+ ErrorKind::Error => write!(f, "error"),
+ ErrorKind::Note => write!(f, "note"),
+ ErrorKind::Suggestion => write!(f, "suggestion"),
+ ErrorKind::Warning => write!(f, "warning"),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct Error {
+ pub line_num: usize,
+ /// What kind of message we expect (e.g. warning, error, suggestion).
+ /// `None` if not specified or unknown message kind.
+ pub kind: Option<ErrorKind>,
+ pub msg: String,
+}
+
+#[derive(PartialEq, Debug)]
+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;
+/// the latter is an "adjusts" that goes that many lines up.
+///
+/// Goal is to enable tests both like: //~^^^ ERROR go up three
+/// and also //~^ ERROR message one for the preceding line, and
+/// //~| ERROR message two for that same line.
+///
+/// If cfg is not None (i.e., in an incremental test), then we look
+/// for `//[X]~` instead, where `X` is the current `cfg`.
+pub fn load_errors(testfile: &Path, cfg: Option<&str>) -> Vec<Error> {
+ let rdr = BufReader::new(File::open(testfile).unwrap());
+
+ // `last_nonfollow_error` tracks the most recently seen
+ // line with an error template that did not use the
+ // follow-syntax, "//~| ...".
+ //
+ // (pnkfelix could not find an easy way to compose Iterator::scan
+ // and Iterator::filter_map to pass along this information into
+ // `parse_expected`. So instead I am storing that state here and
+ // updating it in the map callback below.)
+ let mut last_nonfollow_error = None;
+
+ let tag = match cfg {
+ Some(rev) => format!("//[{}]~", rev),
+ None => "//~".to_string(),
+ };
+
+ 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()
+}
+
+fn parse_expected(last_nonfollow_error: Option<usize>,
+ line_num: usize,
+ line: &str,
+ tag: &str)
+ -> Option<(WhichLine, Error)> {
+ 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 {
+ (false, line[start + tag.len()..].chars().take_while(|c| *c == '^').count())
+ };
+ 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::<ErrorKind>() {
+ 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::<String>();
+ }
+ Err(_) => {
+ // Otherwise we found `//~ foo`:
+ kind = None;
+ let letters = line[kind_start..].chars();
+ msg = letters.skip_while(|c| c.is_whitespace())
+ .collect::<String>();
+ }
+ }
+ let msg = msg.trim().to_owned();
+
+ let (which, line_num) = if follow {
+ assert_eq!(adjusts, 0, "use either //~| or //~^, not both.");
+ let line_num = last_nonfollow_error.expect("encountered //~| without \
+ preceding //~^ line.");
+ (FollowPrevious(line_num), line_num)
+ } else {
+ 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,
+ kind,
+ msg,
+ }))
+}
diff --git a/vendor/compiletest_rs/src/header.rs b/vendor/compiletest_rs/src/header.rs
new file mode 100644
index 000000000..28f3f9de7
--- /dev/null
+++ b/vendor/compiletest_rs/src/header.rs
@@ -0,0 +1,709 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::env;
+use std::fs::File;
+use std::io::BufReader;
+use std::io::prelude::*;
+use std::path::{Path, PathBuf};
+
+use common::Config;
+use common;
+use util;
+
+use extract_gdb_version;
+
+/// Properties which must be known very early, before actually running
+/// the test.
+pub struct EarlyProps {
+ pub ignore: bool,
+ pub should_fail: bool,
+ pub aux: Vec<String>,
+}
+
+impl EarlyProps {
+ pub fn from_file(config: &Config, testfile: &Path) -> Self {
+ let mut props = EarlyProps {
+ ignore: false,
+ should_fail: false,
+ aux: Vec::new(),
+ };
+
+ iter_header(testfile,
+ None,
+ &mut |ln| {
+ props.ignore =
+ props.ignore ||
+ config.parse_cfg_name_directive(ln, "ignore");
+
+ if config.has_cfg_prefix(ln, "only") {
+ props.ignore = match config.parse_cfg_name_directive(ln, "only") {
+ true => props.ignore,
+ false => true,
+ };
+ }
+
+ props.ignore =
+ props.ignore ||
+ ignore_gdb(config, ln) ||
+ ignore_lldb(config, ln) ||
+ ignore_llvm(config, ln);
+
+ if let Some(s) = config.parse_aux_build(ln) {
+ props.aux.push(s);
+ }
+
+ props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail");
+ });
+
+ return props;
+
+ fn ignore_gdb(config: &Config, line: &str) -> bool {
+ if config.mode != common::DebugInfoGdb {
+ return false;
+ }
+
+ if let Some(actual_version) = config.gdb_version {
+ if line.starts_with("min-gdb-version") {
+ let (start_ver, end_ver) = extract_gdb_version_range(line);
+
+ if start_ver != end_ver {
+ panic!("Expected single GDB version")
+ }
+ // Ignore if actual version is smaller the minimum required
+ // version
+ actual_version < start_ver
+ } else if line.starts_with("ignore-gdb-version") {
+ let (min_version, max_version) = extract_gdb_version_range(line);
+
+ if max_version < min_version {
+ panic!("Malformed GDB version range: max < min")
+ }
+
+ actual_version >= min_version && actual_version <= max_version
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }
+
+ // Takes a directive of the form "ignore-gdb-version <version1> [- <version2>]",
+ // returns the numeric representation of <version1> and <version2> as
+ // tuple: (<version1> as u32, <version2> as u32)
+ // If the <version2> part is omitted, the second component of the tuple
+ // is the same as <version1>.
+ fn extract_gdb_version_range(line: &str) -> (u32, u32) {
+ const ERROR_MESSAGE: &'static str = "Malformed GDB version directive";
+
+ let range_components = line.split(&[' ', '-'][..])
+ .filter(|word| !word.is_empty())
+ .map(extract_gdb_version)
+ .skip_while(Option::is_none)
+ .take(3) // 3 or more = invalid, so take at most 3.
+ .collect::<Vec<Option<u32>>>();
+
+ match range_components.len() {
+ 1 => {
+ let v = range_components[0].unwrap();
+ (v, v)
+ }
+ 2 => {
+ let v_min = range_components[0].unwrap();
+ let v_max = range_components[1].expect(ERROR_MESSAGE);
+ (v_min, v_max)
+ }
+ _ => panic!("{}", ERROR_MESSAGE),
+ }
+ }
+
+ fn ignore_lldb(config: &Config, line: &str) -> bool {
+ if config.mode != common::DebugInfoLldb {
+ return false;
+ }
+
+ if let Some(ref actual_version) = config.lldb_version {
+ if line.starts_with("min-lldb-version") {
+ let min_version = line.trim_end()
+ .rsplit(' ')
+ .next()
+ .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)
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }
+
+ fn ignore_llvm(config: &Config, line: &str) -> bool {
+ if config.system_llvm && line.starts_with("no-system-llvm") {
+ return true;
+ }
+ if let Some(ref actual_version) = config.llvm_version {
+ if line.starts_with("min-llvm-version") {
+ let min_version = line.trim_end()
+ .rsplit(' ')
+ .next()
+ .expect("Malformed llvm version directive");
+ // Ignore if actual version is smaller the minimum required
+ // version
+ &actual_version[..] < min_version
+ } else if line.starts_with("min-system-llvm-version") {
+ let min_version = line.trim_end()
+ .rsplit(' ')
+ .next()
+ .expect("Malformed llvm version directive");
+ // Ignore if using system LLVM and actual version
+ // is smaller the minimum required version
+ !(config.system_llvm && &actual_version[..] < min_version)
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct TestProps {
+ // Lines that should be expected, in order, on standard out
+ pub error_patterns: Vec<String>,
+ // Extra flags to pass to the compiler
+ pub compile_flags: Vec<String>,
+ // Extra flags to pass when the compiled code is run (such as --bench)
+ pub run_flags: Option<String>,
+ // If present, the name of a file that this test should match when
+ // pretty-printed
+ pub pp_exact: Option<PathBuf>,
+ // 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<String>,
+ // Environment settings to use for compiling
+ pub rustc_env: Vec<(String, String)>,
+ // Environment settings to use during execution
+ pub exec_env: Vec<(String, String)>,
+ // Lines to check if they appear in the expected debugger output
+ pub check_lines: Vec<String>,
+ // 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
+ pub force_host: bool,
+ // Check stdout for error-pattern output as well as stderr
+ pub check_stdout: bool,
+ // Don't force a --crate-type=dylib flag on the command line
+ pub no_prefer_dynamic: bool,
+ // Run --pretty expanded when running pretty printing tests
+ pub pretty_expanded: bool,
+ // Which pretty mode are we testing with, default to 'normal'
+ pub pretty_mode: String,
+ // Only compare pretty output and don't try compiling
+ pub pretty_compare_only: bool,
+ // Patterns which must not appear in the output of a cfail test.
+ pub forbid_output: Vec<String>,
+ // Revisions to test for incremental compilation.
+ pub revisions: Vec<String>,
+ // Directory (if any) to use for incremental compilation. This is
+ // not set by end-users; rather it is set by the incremental
+ // testing harness and used when generating compilation
+ // arguments. (In particular, it propagates to the aux-builds.)
+ pub incremental_dir: Option<PathBuf>,
+ // Specifies that a cfail test must actually compile without errors.
+ pub must_compile_successfully: bool,
+ // rustdoc will test the output of the `--test` option
+ pub check_test_line_numbers_match: bool,
+ // The test must be compiled and run successfully. Only used in UI tests for
+ // now.
+ pub run_pass: bool,
+ // customized normalization rules
+ pub normalize_stdout: Vec<(String, String)>,
+ pub normalize_stderr: Vec<(String, String)>,
+ pub run_rustfix: bool,
+ pub rustfix_only_machine_applicable: bool,
+ pub assembly_output: Option<String>,
+}
+
+impl TestProps {
+ pub fn new() -> Self {
+ TestProps {
+ error_patterns: vec![],
+ compile_flags: vec![],
+ run_flags: None,
+ pp_exact: None,
+ aux_builds: vec![],
+ revisions: vec![],
+ rustc_env: vec![],
+ exec_env: vec![],
+ check_lines: vec![],
+ build_aux_docs: false,
+ force_host: false,
+ check_stdout: false,
+ no_prefer_dynamic: false,
+ pretty_expanded: false,
+ pretty_mode: "normal".to_string(),
+ pretty_compare_only: false,
+ forbid_output: vec![],
+ incremental_dir: None,
+ must_compile_successfully: false,
+ check_test_line_numbers_match: false,
+ run_pass: false,
+ normalize_stdout: vec![],
+ normalize_stderr: vec![],
+ run_rustfix: false,
+ rustfix_only_machine_applicable: false,
+ assembly_output: None,
+ }
+ }
+
+ pub fn from_aux_file(&self,
+ testfile: &Path,
+ cfg: Option<&str>,
+ config: &Config)
+ -> Self {
+ let mut props = TestProps::new();
+
+ // copy over select properties to the aux build:
+ props.incremental_dir = self.incremental_dir.clone();
+ props.load_from(testfile, cfg, config);
+
+ props
+ }
+
+ pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
+ let mut props = TestProps::new();
+ props.load_from(testfile, cfg, config);
+ props
+ }
+
+ /// Load properties from `testfile` into `props`. If a property is
+ /// tied to a particular revision `foo` (indicated by writing
+ /// `//[foo]`), then the property is ignored unless `cfg` is
+ /// `Some("foo")`.
+ fn load_from(&mut self,
+ testfile: &Path,
+ cfg: Option<&str>,
+ config: &Config) {
+ let mut has_edition = false;
+ iter_header(testfile,
+ cfg,
+ &mut |ln| {
+ if let Some(ep) = config.parse_error_pattern(ln) {
+ self.error_patterns.push(ep);
+ }
+
+ if let Some(flags) = config.parse_compile_flags(ln) {
+ self.compile_flags.extend(flags.split_whitespace()
+ .map(|s| s.to_owned()));
+ }
+
+ if let Some(edition) = config.parse_edition(ln) {
+ self.compile_flags.push(format!("--edition={}", edition));
+ has_edition = true;
+ }
+
+ if let Some(r) = config.parse_revisions(ln) {
+ self.revisions.extend(r);
+ }
+
+ if self.run_flags.is_none() {
+ self.run_flags = config.parse_run_flags(ln);
+ }
+
+ if self.pp_exact.is_none() {
+ self.pp_exact = config.parse_pp_exact(ln, testfile);
+ }
+
+ if !self.build_aux_docs {
+ self.build_aux_docs = config.parse_build_aux_docs(ln);
+ }
+
+ if !self.force_host {
+ self.force_host = config.parse_force_host(ln);
+ }
+
+ if !self.check_stdout {
+ self.check_stdout = config.parse_check_stdout(ln);
+ }
+
+ if !self.no_prefer_dynamic {
+ self.no_prefer_dynamic = config.parse_no_prefer_dynamic(ln);
+ }
+
+ if !self.pretty_expanded {
+ self.pretty_expanded = config.parse_pretty_expanded(ln);
+ }
+
+ if let Some(m) = config.parse_pretty_mode(ln) {
+ self.pretty_mode = m;
+ }
+
+ if !self.pretty_compare_only {
+ self.pretty_compare_only = config.parse_pretty_compare_only(ln);
+ }
+
+ if let Some(ab) = config.parse_aux_build(ln) {
+ self.aux_builds.push(ab);
+ }
+
+ if let Some(ee) = config.parse_env(ln, "exec-env") {
+ self.exec_env.push(ee);
+ }
+
+ if let Some(ee) = config.parse_env(ln, "rustc-env") {
+ self.rustc_env.push(ee);
+ }
+
+ if let Some(cl) = config.parse_check_line(ln) {
+ self.check_lines.push(cl);
+ }
+
+ if let Some(of) = config.parse_forbid_output(ln) {
+ self.forbid_output.push(of);
+ }
+
+ if !self.must_compile_successfully {
+ self.must_compile_successfully = config.parse_must_compile_successfully(ln);
+ }
+
+ if !self.check_test_line_numbers_match {
+ self.check_test_line_numbers_match = config.parse_check_test_line_numbers_match(ln);
+ }
+
+ if !self.run_pass {
+ self.run_pass = config.parse_run_pass(ln);
+ }
+
+ if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") {
+ self.normalize_stdout.push(rule);
+ }
+ if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") {
+ self.normalize_stderr.push(rule);
+ }
+
+ if !self.run_rustfix {
+ self.run_rustfix = config.parse_run_rustfix(ln);
+ }
+
+ if !self.rustfix_only_machine_applicable {
+ self.rustfix_only_machine_applicable =
+ config.parse_rustfix_only_machine_applicable(ln);
+ }
+
+ if self.assembly_output.is_none() {
+ self.assembly_output = config.parse_assembly_output(ln);
+ }
+ });
+
+ for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] {
+ if let Ok(val) = env::var(key) {
+ if self.exec_env.iter().find(|&&(ref x, _)| x == key).is_none() {
+ self.exec_env.push(((*key).to_owned(), val))
+ }
+ }
+ }
+
+ if let (Some(edition), false) = (&config.edition, has_edition) {
+ self.compile_flags.push(format!("--edition={}", edition));
+ }
+ }
+}
+
+fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut dyn FnMut(&str)) {
+ if testfile.is_dir() {
+ return;
+ }
+ let rdr = BufReader::new(File::open(testfile).unwrap());
+ for ln in rdr.lines() {
+ // Assume that any directives will be found before the first
+ // module or function. This doesn't seem to be an optimization
+ // with a warm page cache. Maybe with a cold one.
+ let ln = ln.unwrap();
+ let ln = ln.trim();
+ if ln.starts_with("fn") || ln.starts_with("mod") {
+ return;
+ } else if ln.starts_with("//[") {
+ // A comment like `//[foo]` is specific to revision `foo`
+ if let Some(close_brace) = ln.find(']') {
+ let lncfg = &ln[3..close_brace];
+ let matches = match cfg {
+ Some(s) => s == &lncfg[..],
+ None => false,
+ };
+ if matches {
+ it(ln[(close_brace + 1) ..].trim_start());
+ }
+ } else {
+ panic!("malformed condition directive: expected `//[foo]`, found `{}`",
+ ln)
+ }
+ } else if ln.starts_with("//") {
+ it(ln[2..].trim_start());
+ }
+ }
+ return;
+}
+
+impl Config {
+ fn parse_error_pattern(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "error-pattern")
+ }
+
+ fn parse_forbid_output(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "forbid-output")
+ }
+
+ fn parse_aux_build(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "aux-build")
+ .map(|r| r.trim().to_string())
+ }
+
+ fn parse_compile_flags(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "compile-flags")
+ }
+
+ fn parse_revisions(&self, line: &str) -> Option<Vec<String>> {
+ self.parse_name_value_directive(line, "revisions")
+ .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
+ }
+
+ fn parse_run_flags(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "run-flags")
+ }
+
+ fn parse_check_line(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "check")
+ }
+
+ fn parse_force_host(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "force-host")
+ }
+
+ fn parse_build_aux_docs(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "build-aux-docs")
+ }
+
+ fn parse_check_stdout(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "check-stdout")
+ }
+
+ fn parse_no_prefer_dynamic(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "no-prefer-dynamic")
+ }
+
+ fn parse_pretty_expanded(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "pretty-expanded")
+ }
+
+ fn parse_pretty_mode(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "pretty-mode")
+ }
+
+ fn parse_pretty_compare_only(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "pretty-compare-only")
+ }
+
+ fn parse_must_compile_successfully(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "must-compile-successfully")
+ }
+
+ fn parse_check_test_line_numbers_match(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "check-test-line-numbers-match")
+ }
+
+ fn parse_run_pass(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "run-pass")
+ }
+
+ fn parse_assembly_output(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "assembly-output")
+ .map(|r| r.trim().to_string())
+ }
+
+ fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
+ self.parse_name_value_directive(line, name).map(|nv| {
+ // nv is either FOO or FOO=BAR
+ let mut strs: Vec<String> = nv.splitn(2, '=')
+ .map(str::to_owned)
+ .collect();
+
+ match strs.len() {
+ 1 => (strs.pop().unwrap(), "".to_owned()),
+ 2 => {
+ let end = strs.pop().unwrap();
+ (strs.pop().unwrap(), end)
+ }
+ n => panic!("Expected 1 or 2 strings, not {}", n),
+ }
+ })
+ }
+
+ fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> {
+ if let Some(s) = self.parse_name_value_directive(line, "pp-exact") {
+ Some(PathBuf::from(&s))
+ } else if self.parse_name_directive(line, "pp-exact") {
+ testfile.file_name().map(PathBuf::from)
+ } else {
+ None
+ }
+ }
+
+ fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> {
+ if self.parse_cfg_name_directive(line, prefix) {
+ let from = match parse_normalization_string(&mut line) {
+ Some(s) => s,
+ None => return None,
+ };
+ let to = match parse_normalization_string(&mut line) {
+ Some(s) => s,
+ None => return None,
+ };
+ Some((from, to))
+ } else {
+ None
+ }
+ }
+
+ /// Parses a name-value directive which contains config-specific information, e.g. `ignore-x86`
+ /// or `normalize-stderr-32bit`. Returns `true` if the line matches it.
+ fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> bool {
+ if line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') {
+ let name = line[prefix.len()+1 ..].split(&[':', ' '][..]).next().unwrap();
+
+ name == "test" ||
+ util::matches_os(&self.target, name) || // target
+ name == util::get_arch(&self.target) || // architecture
+ name == util::get_pointer_width(&self.target) || // pointer width
+ name == self.stage_id.split('-').next().unwrap() || // stage
+ Some(name) == util::get_env(&self.target) || // env
+ self.target.ends_with(name) || // target and env
+ match self.mode {
+ common::DebugInfoGdb => name == "gdb",
+ common::DebugInfoLldb => name == "lldb",
+ common::Pretty => name == "pretty",
+ _ => false,
+ } ||
+ (self.target != self.host && name == "cross-compile")
+ } else {
+ false
+ }
+ }
+
+ fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool {
+ // returns whether this line contains this prefix or not. For prefix
+ // "ignore", returns true if line says "ignore-x86_64", "ignore-arch",
+ // "ignore-android" etc.
+ line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-')
+ }
+
+ fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
+ // Ensure the directive is a whole word. Do not match "ignore-x86" when
+ // the line says "ignore-x86_64".
+ line.starts_with(directive) && match line.as_bytes().get(directive.len()) {
+ None | Some(&b' ') | Some(&b':') => true,
+ _ => false
+ }
+ }
+
+ pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
+ let colon = directive.len();
+ if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
+ let value = line[(colon + 1) ..].to_owned();
+ debug!("{}: {}", directive, value);
+ Some(expand_variables(value, self))
+ } else {
+ None
+ }
+ }
+
+ pub fn find_rust_src_root(&self) -> Option<PathBuf> {
+ let mut path = self.src_base.clone();
+ let path_postfix = Path::new("src/etc/lldb_batchmode.py");
+
+ while path.pop() {
+ if path.join(&path_postfix).is_file() {
+ return Some(path);
+ }
+ }
+
+ None
+ }
+
+ fn parse_run_rustfix(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "run-rustfix")
+ }
+
+ fn parse_rustfix_only_machine_applicable(&self, line: &str) -> bool {
+ self.parse_name_directive(line, "rustfix-only-machine-applicable")
+ }
+
+ fn parse_edition(&self, line: &str) -> Option<String> {
+ self.parse_name_value_directive(line, "edition")
+ }
+}
+
+pub fn lldb_version_to_int(version_string: &str) -> isize {
+ let error_string = format!("Encountered LLDB version string with unexpected format: {}",
+ version_string);
+ version_string.parse().expect(&error_string)
+}
+
+fn expand_variables(mut value: String, config: &Config) -> String {
+ const CWD: &'static str = "{{cwd}}";
+ const SRC_BASE: &'static str = "{{src-base}}";
+ const BUILD_BASE: &'static str = "{{build-base}}";
+
+ if value.contains(CWD) {
+ let cwd = env::current_dir().unwrap();
+ value = value.replace(CWD, &cwd.to_string_lossy());
+ }
+
+ if value.contains(SRC_BASE) {
+ value = value.replace(SRC_BASE, &config.src_base.to_string_lossy());
+ }
+
+ if value.contains(BUILD_BASE) {
+ value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy());
+ }
+
+ value
+}
+
+/// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line`
+/// variable after the end of the quoted string.
+///
+/// # Examples
+///
+/// ```ignore
+/// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\".";
+/// let first = parse_normalization_string(&mut s);
+/// assert_eq!(first, Some("something (32 bits)".to_owned()));
+/// assert_eq!(s, " -> \"something ($WORD bits)\".");
+/// ```
+fn parse_normalization_string(line: &mut &str) -> Option<String> {
+ // FIXME support escapes in strings.
+ let begin = match line.find('"') {
+ Some(i) => i + 1,
+ None => return None,
+ };
+ let end = match line[begin..].find('"') {
+ Some(i) => i + begin,
+ None => return None,
+ };
+ let result = line[begin..end].to_owned();
+ *line = &line[end+1..];
+ Some(result)
+}
diff --git a/vendor/compiletest_rs/src/json.rs b/vendor/compiletest_rs/src/json.rs
new file mode 100644
index 000000000..6f9e2ff10
--- /dev/null
+++ b/vendor/compiletest_rs/src/json.rs
@@ -0,0 +1,263 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use errors::{Error, ErrorKind};
+use serde_json;
+use std::str::FromStr;
+use std::path::Path;
+use runtest::ProcRes;
+
+// These structs are a subset of the ones found in
+// `syntax::json`.
+
+#[derive(Deserialize)]
+struct Diagnostic {
+ message: String,
+ code: Option<DiagnosticCode>,
+ level: String,
+ spans: Vec<DiagnosticSpan>,
+ children: Vec<Diagnostic>,
+ rendered: Option<String>,
+}
+
+#[derive(Deserialize, Clone)]
+struct DiagnosticSpan {
+ file_name: String,
+ line_start: usize,
+ line_end: usize,
+ column_start: usize,
+ column_end: usize,
+ is_primary: bool,
+ label: Option<String>,
+ suggested_replacement: Option<String>,
+ expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
+}
+
+impl DiagnosticSpan {
+ /// Returns the deepest source span in the macro call stack with a given file name.
+ /// This is either the supplied span, or the span for some macro callsite that expanded to it.
+ fn first_callsite_in_file(&self, file_name: &str) -> &DiagnosticSpan {
+ if self.file_name == file_name {
+ self
+ } else {
+ self.expansion
+ .as_ref()
+ .map(|origin| origin.span.first_callsite_in_file(file_name))
+ .unwrap_or(self)
+ }
+ }
+}
+
+#[derive(Deserialize, Clone)]
+struct DiagnosticSpanMacroExpansion {
+ /// span where macro was applied to generate this code
+ span: DiagnosticSpan,
+
+ /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
+ macro_decl_name: String,
+}
+
+#[derive(Deserialize, Clone)]
+struct DiagnosticCode {
+ /// The code itself.
+ code: String,
+ /// An explanation for the code.
+ explanation: Option<String>,
+}
+
+pub fn extract_rendered(output: &str, proc_res: &ProcRes) -> String {
+ output
+ .lines()
+ .filter_map(|line| {
+ if line.starts_with('{') {
+ match serde_json::from_str::<Diagnostic>(line) {
+ Ok(diagnostic) => diagnostic.rendered,
+ Err(error) => {
+ proc_res.fatal(Some(&format!(
+ "failed to decode compiler output as json: \
+ `{}`\nline: {}\noutput: {}",
+ error, line, output
+ )));
+ }
+ }
+ } else {
+ // preserve non-JSON lines, such as ICEs
+ Some(format!("{}\n", line))
+ }
+ })
+ .collect()
+}
+
+pub fn parse_output(file_name: &str, output: &str, proc_res: &ProcRes) -> Vec<Error> {
+ output.lines()
+ .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<Error> {
+ // The compiler sometimes intermingles non-JSON stuff into the
+ // output. This hack just skips over such lines. Yuck.
+ if line.starts_with('{') {
+ match serde_json::from_str::<Diagnostic>(line) {
+ Ok(diagnostic) => {
+ let mut expected_errors = vec![];
+ push_expected_errors(&mut expected_errors, &diagnostic, &[], file_name);
+ expected_errors
+ }
+ Err(error) => {
+ proc_res.fatal(Some(&format!("failed to decode compiler output as json: \
+ `{}`\noutput: {}\nline: {}",
+ error,
+ line,
+ output)));
+ }
+ }
+ } else {
+ vec![]
+ }
+}
+
+fn push_expected_errors(expected_errors: &mut Vec<Error>,
+ diagnostic: &Diagnostic,
+ default_spans: &[&DiagnosticSpan],
+ file_name: &str) {
+ // In case of macro expansions, we need to get the span of the callsite
+ let spans_info_in_this_file: Vec<_> = diagnostic
+ .spans
+ .iter()
+ .map(|span| (span.is_primary, span.first_callsite_in_file(file_name)))
+ .filter(|(_, span)| Path::new(&span.file_name) == Path::new(&file_name))
+ .collect();
+
+ let spans_in_this_file: Vec<_> = spans_info_in_this_file.iter()
+ .map(|(_, span)| span)
+ .collect();
+
+ let primary_spans: Vec<_> = spans_info_in_this_file.iter()
+ .filter(|(is_primary, _)| *is_primary)
+ .map(|(_, span)| span)
+ .take(1) // sometimes we have more than one showing up in the json; pick first
+ .cloned()
+ .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
+ default_spans
+ } else {
+ &primary_spans
+ };
+
+ // We break the output into multiple lines, and then append the
+ // [E123] to every line in the output. This may be overkill. The
+ // intention was to match existing tests that do things like "//|
+ // found `i32` [E123]" and expect to match that somewhere, and yet
+ // also ensure that `//~ ERROR E123` *always* works. The
+ // assumption is that these multi-line error messages are on their
+ // way out anyhow.
+ let with_code = |span: &DiagnosticSpan, text: &str| {
+ match diagnostic.code {
+ Some(ref code) =>
+ // FIXME(#33000) -- it'd be better to use a dedicated
+ // UI harness than to include the line/col number like
+ // this, but some current tests rely on it.
+ //
+ // Note: Do NOT include the filename. These can easily
+ // cause false matches where the expected message
+ // appears in the filename, and hence the message
+ // changes but the test still passes.
+ format!("{}:{}: {}:{}: {} [{}]",
+ span.line_start, span.column_start,
+ span.line_end, span.column_end,
+ text, code.code.clone()),
+ None =>
+ // FIXME(#33000) -- it'd be better to use a dedicated UI harness
+ format!("{}:{}: {}:{}: {}",
+ span.line_start, span.column_start,
+ span.line_end, span.column_end,
+ text),
+ }
+ };
+
+ // Convert multi-line messages into multiple expected
+ // errors. We expect to replace these with something
+ // more structured shortly anyhow.
+ let mut message_lines = diagnostic.message.lines();
+ if let Some(first_line) = message_lines.next() {
+ 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,
+ 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),
+ });
+ }
+ }
+
+ // If the message has a suggestion, register that.
+ for span in primary_spans {
+ if let Some(ref suggested_replacement) = span.suggested_replacement {
+ for (index, line) in suggested_replacement.lines().enumerate() {
+ expected_errors.push(Error {
+ line_num: span.line_start + 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);
+ }
+ }
+
+ // Add notes for any labels that appear in the message.
+ for span in spans_in_this_file.iter()
+ .filter(|span| span.label.is_some()) {
+ expected_errors.push(Error {
+ line_num: span.line_start,
+ kind: Some(ErrorKind::Note),
+ msg: span.label.clone().unwrap(),
+ });
+ }
+
+ // Flatten out the children.
+ for child in &diagnostic.children {
+ push_expected_errors(expected_errors, child, primary_spans, file_name);
+ }
+}
+
+fn push_backtrace(expected_errors: &mut Vec<Error>,
+ 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),
+ });
+ }
+
+ for previous_expansion in &expansion.span.expansion {
+ push_backtrace(expected_errors, previous_expansion, file_name);
+ }
+}
diff --git a/vendor/compiletest_rs/src/lib.rs b/vendor/compiletest_rs/src/lib.rs
new file mode 100644
index 000000000..edfc0208d
--- /dev/null
+++ b/vendor/compiletest_rs/src/lib.rs
@@ -0,0 +1,458 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_type = "lib"]
+#![cfg_attr(feature = "rustc", feature(rustc_private))]
+#![cfg_attr(feature = "rustc", feature(test))]
+#![deny(unused_imports)]
+
+#[cfg(feature = "rustc")]
+extern crate rustc_session;
+
+#[cfg(unix)]
+extern crate libc;
+#[cfg(feature = "rustc")]
+extern crate test;
+#[cfg(not(feature = "rustc"))]
+extern crate tester as test;
+
+#[cfg(feature = "tmp")]
+extern crate tempfile;
+
+#[macro_use]
+extern crate log;
+extern crate diff;
+extern crate filetime;
+extern crate regex;
+extern crate serde_json;
+#[macro_use]
+extern crate serde_derive;
+extern crate rustfix;
+
+use common::{DebugInfoGdb, DebugInfoLldb, Pretty};
+use common::{Mode, TestPaths};
+use std::env;
+use std::ffi::OsString;
+use std::fs;
+use std::io;
+use std::path::{Path, PathBuf};
+
+use self::header::EarlyProps;
+
+pub mod common;
+pub mod errors;
+pub mod header;
+mod json;
+mod read2;
+pub mod runtest;
+pub mod uidiff;
+pub mod util;
+
+pub use common::Config;
+
+pub fn run_tests(config: &Config) {
+ if config.target.contains("android") {
+ if let DebugInfoGdb = config.mode {
+ println!(
+ "{} debug-info test uses tcp 5039 port.\
+ please reserve it",
+ config.target
+ );
+ }
+
+ // android debug-info test uses remote debugger
+ // so, we test 1 thread at once.
+ // also trying to isolate problems with adb_run_wrapper.sh ilooping
+ env::set_var("RUST_TEST_THREADS", "1");
+ }
+
+ if let DebugInfoLldb = config.mode {
+ // Some older versions of LLDB seem to have problems with multiple
+ // instances running in parallel, so only run one test task at a
+ // time.
+ env::set_var("RUST_TEST_TASKS", "1");
+ }
+
+ // If we want to collect rustfix coverage information,
+ // we first make sure that the coverage file does not exist.
+ // It will be created later on.
+ if config.rustfix_coverage {
+ let mut coverage_file_path = config.build_base.clone();
+ coverage_file_path.push("rustfix_missing_coverage.txt");
+ if coverage_file_path.exists() {
+ if let Err(e) = fs::remove_file(&coverage_file_path) {
+ panic!(
+ "Could not delete {} due to {}",
+ coverage_file_path.display(),
+ e
+ )
+ }
+ }
+ }
+ let opts = test_opts(config);
+ let tests = make_tests(config);
+ // sadly osx needs some file descriptor limits raised for running tests in
+ // parallel (especially when we have lots and lots of child processes).
+ // For context, see #8904
+ // unsafe { raise_fd_limit::raise_fd_limit(); }
+ // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
+ // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
+ env::set_var("__COMPAT_LAYER", "RunAsInvoker");
+ let res = test::run_tests_console(&opts, tests.into_iter().collect());
+ match res {
+ Ok(true) => {}
+ Ok(false) => panic!("Some tests failed"),
+ Err(e) => {
+ println!("I/O failure during tests: {:?}", e);
+ }
+ }
+}
+
+pub fn test_opts(config: &Config) -> test::TestOpts {
+ test::TestOpts {
+ filters: config.filters.clone(),
+ filter_exact: config.filter_exact,
+ exclude_should_panic: false,
+ force_run_in_process: false,
+ run_ignored: if config.run_ignored {
+ test::RunIgnored::Yes
+ } else {
+ test::RunIgnored::No
+ },
+ format: if config.quiet {
+ test::OutputFormat::Terse
+ } else {
+ test::OutputFormat::Pretty
+ },
+ logfile: config.logfile.clone(),
+ run_tests: true,
+ bench_benchmarks: true,
+ nocapture: match env::var("RUST_TEST_NOCAPTURE") {
+ Ok(val) => &val != "0",
+ Err(_) => false,
+ },
+ color: test::AutoColor,
+ test_threads: None,
+ skip: vec![],
+ list: false,
+ options: test::Options::new(),
+ time_options: None,
+ #[cfg(feature = "rustc")]
+ shuffle: false,
+ #[cfg(feature = "rustc")]
+ shuffle_seed: None,
+ }
+}
+
+pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
+ debug!("making tests from {:?}", config.src_base.display());
+ let mut tests = Vec::new();
+ collect_tests_from_dir(
+ config,
+ &config.src_base,
+ &config.src_base,
+ &PathBuf::new(),
+ &mut tests,
+ )
+ .unwrap();
+ tests
+}
+
+fn collect_tests_from_dir(
+ config: &Config,
+ base: &Path,
+ dir: &Path,
+ relative_dir_path: &Path,
+ tests: &mut Vec<test::TestDescAndFn>,
+) -> io::Result<()> {
+ // Ignore directories that contain a file
+ // `compiletest-ignore-dir`.
+ for file in fs::read_dir(dir)? {
+ let file = file?;
+ 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 = fs::read_dir(dir)?;
+ for file in dirs {
+ let file = file?;
+ let file_path = file.path();
+ 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
+ // 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();
+
+ let paths = TestPaths {
+ file: file_path,
+ base: base.to_path_buf(),
+ relative_dir: relative_dir_path.to_path_buf(),
+ };
+ tests.push(make_test(config, &paths))
+ } else if file_path.is_dir() {
+ let relative_file_path = relative_dir_path.join(file.file_name());
+ 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());
+ 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(file_name: &OsString) -> bool {
+ let file_name = file_name.to_str().unwrap();
+
+ if !file_name.ends_with(".rs") {
+ return false;
+ }
+
+ // `.`, `#`, 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 {
+ let early_props = EarlyProps::from_file(config, &testpaths.file);
+
+ // The `should-fail` annotation doesn't apply to pretty tests,
+ // since we run the pretty printer across all tests by default.
+ // If desired, we could add a `should-fail-pretty` annotation.
+ let should_panic = match config.mode {
+ Pretty => test::ShouldPanic::No,
+ _ => {
+ if early_props.should_fail {
+ test::ShouldPanic::Yes
+ } else {
+ test::ShouldPanic::No
+ }
+ }
+ };
+
+ test::TestDescAndFn {
+ desc: test::TestDesc {
+ name: make_test_name(config, testpaths),
+ ignore: early_props.ignore,
+ should_panic: should_panic,
+ #[cfg(not(feature = "rustc"))]
+ allow_fail: false,
+ #[cfg(feature = "rustc")]
+ compile_fail: false,
+ #[cfg(feature = "rustc")]
+ no_run: false,
+ test_type: test::TestType::IntegrationTest,
+ #[cfg(feature = "rustc")]
+ ignore_message: None,
+ },
+ testfn: make_test_closure(config, testpaths),
+ }
+}
+
+fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf {
+ let stamp_name = format!(
+ "{}-{}.stamp",
+ testpaths.file.file_name().unwrap().to_str().unwrap(),
+ config.stage_id
+ );
+ config
+ .build_base
+ .canonicalize()
+ .unwrap_or_else(|_| config.build_base.clone())
+ .join(stamp_name)
+}
+
+pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
+ // Convert a complete path to something like
+ //
+ // run-pass/foo/bar/baz.rs
+ let path = PathBuf::from(config.src_base.file_name().unwrap())
+ .join(&testpaths.relative_dir)
+ .join(&testpaths.file.file_name().unwrap());
+ test::DynTestName(format!("[{}] {}", config.mode, path.display()))
+}
+
+pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
+ let config = config.clone();
+ let testpaths = testpaths.clone();
+ test::DynTestFn(Box::new(move || {
+ let config = config.clone(); // FIXME: why is this needed?
+ runtest::run(config, &testpaths)
+ }))
+}
+
+fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
+ let full_version_line = full_version_line.trim();
+
+ // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
+ // of the ? sections being optional
+
+ // We will parse up to 3 digits for minor and patch, ignoring the date
+ // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
+
+ // don't start parsing in the middle of a number
+ let mut prev_was_digit = false;
+ for (pos, c) in full_version_line.char_indices() {
+ if prev_was_digit || !c.is_digit(10) {
+ prev_was_digit = c.is_digit(10);
+ continue;
+ }
+
+ prev_was_digit = true;
+
+ let line = &full_version_line[pos..];
+
+ let next_split = match line.find(|c: char| !c.is_digit(10)) {
+ Some(idx) => idx,
+ None => continue, // no minor version
+ };
+
+ if line.as_bytes()[next_split] != b'.' {
+ continue; // no minor version
+ }
+
+ let major = &line[..next_split];
+ let line = &line[next_split + 1..];
+
+ let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
+ Some(idx) => {
+ if line.as_bytes()[idx] == b'.' {
+ let patch = &line[idx + 1..];
+
+ let patch_len = patch
+ .find(|c: char| !c.is_digit(10))
+ .unwrap_or_else(|| patch.len());
+ let patch = &patch[..patch_len];
+ let patch = if patch_len > 3 || patch_len == 0 {
+ None
+ } else {
+ Some(patch)
+ };
+
+ (&line[..idx], patch)
+ } else {
+ (&line[..idx], None)
+ }
+ }
+ None => (line, None),
+ };
+
+ if major.len() != 1 || minor.is_empty() {
+ continue;
+ }
+
+ let major: u32 = major.parse().unwrap();
+ let minor: u32 = minor.parse().unwrap();
+ let patch: u32 = patch.unwrap_or("0").parse().unwrap();
+
+ return Some(((major * 1000) + minor) * 1000 + patch);
+ }
+
+ None
+}
+
+#[allow(dead_code)]
+fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
+ // Extract the major LLDB version from the given version string.
+ // LLDB version strings are different for Apple and non-Apple platforms.
+ // At the moment, this function only supports the Apple variant, which looks
+ // like this:
+ //
+ // LLDB-179.5 (older versions)
+ // lldb-300.2.51 (new versions)
+ //
+ // We are only interested in the major version number, so this function
+ // will return `Some("179")` and `Some("300")` respectively.
+
+ if let Some(ref full_version_line) = full_version_line {
+ if !full_version_line.trim().is_empty() {
+ let full_version_line = full_version_line.trim();
+
+ for (pos, l) in full_version_line.char_indices() {
+ if l != 'l' && l != 'L' {
+ continue;
+ }
+ if pos + 5 >= full_version_line.len() {
+ continue;
+ }
+ let l = full_version_line[pos + 1..].chars().next().unwrap();
+ if l != 'l' && l != 'L' {
+ continue;
+ }
+ let d = full_version_line[pos + 2..].chars().next().unwrap();
+ if d != 'd' && d != 'D' {
+ continue;
+ }
+ let b = full_version_line[pos + 3..].chars().next().unwrap();
+ if b != 'b' && b != 'B' {
+ continue;
+ }
+ let dash = full_version_line[pos + 4..].chars().next().unwrap();
+ if dash != '-' {
+ continue;
+ }
+
+ let vers = full_version_line[pos + 5..]
+ .chars()
+ .take_while(|c| c.is_digit(10))
+ .collect::<String>();
+ if !vers.is_empty() {
+ return Some(vers);
+ }
+ }
+ println!(
+ "Could not extract LLDB version from line '{}'",
+ full_version_line
+ );
+ }
+ }
+ None
+}
+
+#[allow(dead_code)]
+fn is_blacklisted_lldb_version(version: &str) -> bool {
+ version == "350"
+}
diff --git a/vendor/compiletest_rs/src/raise_fd_limit.rs b/vendor/compiletest_rs/src/raise_fd_limit.rs
new file mode 100644
index 000000000..fcc5a727c
--- /dev/null
+++ b/vendor/compiletest_rs/src/raise_fd_limit.rs
@@ -0,0 +1,69 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/// darwin_fd_limit exists to work around an issue where launchctl on macOS
+/// defaults the rlimit maxfiles to 256/unlimited. The default soft limit of 256
+/// ends up being far too low for our multithreaded scheduler testing, depending
+/// on the number of cores available.
+///
+/// This fixes issue #7772.
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[allow(non_camel_case_types)]
+pub unsafe fn raise_fd_limit() {
+ use libc;
+ use std::cmp;
+ use std::io;
+ use std::mem::size_of_val;
+ use std::ptr::null_mut;
+
+ static CTL_KERN: libc::c_int = 1;
+ static KERN_MAXFILESPERPROC: libc::c_int = 29;
+
+ // The strategy here is to fetch the current resource limits, read the
+ // kern.maxfilesperproc sysctl value, and bump the soft resource limit for
+ // maxfiles up to the sysctl value.
+
+ // Fetch the kern.maxfilesperproc value
+ let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC];
+ let mut maxfiles: libc::c_int = 0;
+ let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t;
+ if libc::sysctl(&mut mib[0],
+ 2,
+ &mut maxfiles as *mut _ as *mut _,
+ &mut size,
+ null_mut(),
+ 0) != 0 {
+ let err = io::Error::last_os_error();
+ panic!("raise_fd_limit: error calling sysctl: {}", err);
+ }
+
+ // Fetch the current resource limits
+ let mut rlim = libc::rlimit {
+ rlim_cur: 0,
+ rlim_max: 0,
+ };
+ if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 {
+ let err = io::Error::last_os_error();
+ panic!("raise_fd_limit: error calling getrlimit: {}", err);
+ }
+
+ // Bump the soft limit to the smaller of kern.maxfilesperproc and the hard
+ // limit
+ rlim.rlim_cur = cmp::min(maxfiles as libc::rlim_t, rlim.rlim_max);
+
+ // Set our newly-increased resource limit
+ if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) != 0 {
+ let err = io::Error::last_os_error();
+ panic!("raise_fd_limit: error calling setrlimit: {}", err);
+ }
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+pub unsafe fn raise_fd_limit() {}
diff --git a/vendor/compiletest_rs/src/read2.rs b/vendor/compiletest_rs/src/read2.rs
new file mode 100644
index 000000000..664572521
--- /dev/null
+++ b/vendor/compiletest_rs/src/read2.rs
@@ -0,0 +1,208 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// FIXME: This is a complete copy of `cargo/src/cargo/util/read2.rs`
+// Consider unify the read2() in libstd, cargo and this to prevent further code duplication.
+
+pub use self::imp::read2;
+
+#[cfg(not(any(unix, windows)))]
+mod imp {
+ use std::io::{self, Read};
+ use std::process::{ChildStdout, ChildStderr};
+
+ pub fn read2(out_pipe: ChildStdout,
+ err_pipe: ChildStderr,
+ data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
+ let mut buffer = Vec::new();
+ out_pipe.read_to_end(&mut buffer)?;
+ data(true, &mut buffer, true);
+ buffer.clear();
+ err_pipe.read_to_end(&mut buffer)?;
+ data(false, &mut buffer, true);
+ Ok(())
+ }
+}
+
+#[cfg(unix)]
+mod imp {
+ use std::io::prelude::*;
+ use std::io;
+ use std::mem;
+ use std::os::unix::prelude::*;
+ use std::process::{ChildStdout, ChildStderr};
+ use libc;
+
+ pub fn read2(mut out_pipe: ChildStdout,
+ mut err_pipe: ChildStderr,
+ data: &mut dyn FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
+ unsafe {
+ libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
+ libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
+ }
+
+ let mut out_done = false;
+ let mut err_done = false;
+ let mut out = Vec::new();
+ let mut err = Vec::new();
+
+ let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
+ fds[0].fd = out_pipe.as_raw_fd();
+ fds[0].events = libc::POLLIN;
+ fds[1].fd = err_pipe.as_raw_fd();
+ fds[1].events = libc::POLLIN;
+ loop {
+ // wait for either pipe to become readable using `select`
+ let r = unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) };
+ if r == -1 {
+ let err = io::Error::last_os_error();
+ if err.kind() == io::ErrorKind::Interrupted {
+ continue
+ }
+ return Err(err)
+ }
+
+ // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
+ // EAGAIN. If we hit EOF, then this will happen because the underlying
+ // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
+ // this case we flip the other fd back into blocking mode and read
+ // whatever's leftover on that file descriptor.
+ let handle = |res: io::Result<_>| {
+ match res {
+ Ok(_) => Ok(true),
+ Err(e) => {
+ if e.kind() == io::ErrorKind::WouldBlock {
+ Ok(false)
+ } else {
+ Err(e)
+ }
+ }
+ }
+ };
+ if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
+ out_done = true;
+ }
+ data(true, &mut out, out_done);
+ if !err_done && fds[1].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
+ err_done = true;
+ }
+ data(false, &mut err, err_done);
+
+ if out_done && err_done {
+ return Ok(())
+ }
+ }
+ }
+}
+
+#[cfg(windows)]
+mod imp {
+ extern crate miow;
+ extern crate winapi;
+
+ use std::io;
+ use std::os::windows::prelude::*;
+ use std::process::{ChildStdout, ChildStderr};
+ use std::slice;
+
+ use self::miow::iocp::{CompletionPort, CompletionStatus};
+ use self::miow::pipe::NamedPipe;
+ use self::miow::Overlapped;
+ use self::winapi::shared::winerror::ERROR_BROKEN_PIPE;
+
+ struct Pipe<'a> {
+ dst: &'a mut Vec<u8>,
+ overlapped: Overlapped,
+ pipe: NamedPipe,
+ done: bool,
+ }
+
+ pub fn read2(out_pipe: ChildStdout,
+ err_pipe: ChildStderr,
+ data: &mut FnMut(bool, &mut Vec<u8>, bool)) -> io::Result<()> {
+ let mut out = Vec::new();
+ let mut err = Vec::new();
+
+ let port = CompletionPort::new(1)?;
+ port.add_handle(0, &out_pipe)?;
+ port.add_handle(1, &err_pipe)?;
+
+ unsafe {
+ let mut out_pipe = Pipe::new(out_pipe, &mut out);
+ let mut err_pipe = Pipe::new(err_pipe, &mut err);
+
+ out_pipe.read()?;
+ err_pipe.read()?;
+
+ let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];
+
+ while !out_pipe.done || !err_pipe.done {
+ for status in port.get_many(&mut status, None)? {
+ if status.token() == 0 {
+ out_pipe.complete(status);
+ data(true, out_pipe.dst, out_pipe.done);
+ out_pipe.read()?;
+ } else {
+ err_pipe.complete(status);
+ data(false, err_pipe.dst, err_pipe.done);
+ err_pipe.read()?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+ }
+
+ impl<'a> Pipe<'a> {
+ unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
+ Pipe {
+ dst: dst,
+ pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
+ overlapped: Overlapped::zero(),
+ done: false,
+ }
+ }
+
+ unsafe fn read(&mut self) -> io::Result<()> {
+ let dst = slice_to_end(self.dst);
+ match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
+ Ok(_) => Ok(()),
+ Err(e) => {
+ if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
+ self.done = true;
+ Ok(())
+ } else {
+ Err(e)
+ }
+ }
+ }
+ }
+
+ unsafe fn complete(&mut self, status: &CompletionStatus) {
+ let prev = self.dst.len();
+ self.dst.set_len(prev + status.bytes_transferred() as usize);
+ if status.bytes_transferred() == 0 {
+ self.done = true;
+ }
+ }
+ }
+
+ unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
+ if v.capacity() == 0 {
+ v.reserve(16);
+ }
+ if v.capacity() == v.len() {
+ v.reserve(1);
+ }
+ slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize),
+ v.capacity() - v.len())
+ }
+}
diff --git a/vendor/compiletest_rs/src/runtest.rs b/vendor/compiletest_rs/src/runtest.rs
new file mode 100644
index 000000000..c52551488
--- /dev/null
+++ b/vendor/compiletest_rs/src/runtest.rs
@@ -0,0 +1,2894 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use common::{Config, TestPaths};
+use common::{expected_output_path, UI_FIXED, UI_STDERR, UI_STDOUT};
+use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
+use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
+use common::{Incremental, RunMake, Ui, MirOpt, Assembly};
+use diff;
+use errors::{self, ErrorKind, Error};
+use filetime::FileTime;
+use json;
+use regex::Regex;
+use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
+use header::TestProps;
+use crate::util::{logv, PathBufExt};
+
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::env;
+use std::ffi::OsString;
+use std::fs::{self, File, create_dir_all, OpenOptions};
+use std::fmt;
+use std::io::prelude::*;
+use std::io::{self, BufReader};
+use std::path::{Path, PathBuf};
+use std::process::{Command, Output, ExitStatus, Stdio, Child};
+use std::str;
+
+use extract_gdb_version;
+
+/// The name of the environment variable that holds dynamic library locations.
+pub fn dylib_env_var() -> &'static str {
+ if cfg!(windows) {
+ "PATH"
+ } else if cfg!(target_os = "macos") {
+ "DYLD_LIBRARY_PATH"
+ } else if cfg!(target_os = "haiku") {
+ "LIBRARY_PATH"
+ } else {
+ "LD_LIBRARY_PATH"
+ }
+}
+
+pub fn run(config: Config, testpaths: &TestPaths) {
+ match &*config.target {
+
+ "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
+ if !config.adb_device_status {
+ panic!("android device not available");
+ }
+ }
+
+ _ => {
+ // android has its own gdb handling
+ if config.mode == DebugInfoGdb && config.gdb.is_none() {
+ panic!("gdb not available but debuginfo gdb debuginfo test requested");
+ }
+ }
+ }
+
+ if config.verbose {
+ // We're going to be dumping a lot of info. Start on a new line.
+ print!("\n\n");
+ }
+ debug!("running {:?}", testpaths.file.display());
+ let base_props = TestProps::from_file(&testpaths.file, None, &config);
+
+ let base_cx = TestCx { config: &config,
+ props: &base_props,
+ testpaths,
+ revision: None };
+ base_cx.init_all();
+
+ if base_props.revisions.is_empty() {
+ base_cx.run_revision()
+ } else {
+ for revision in &base_props.revisions {
+ let revision_props = TestProps::from_file(&testpaths.file,
+ Some(revision),
+ &config);
+ let rev_cx = TestCx {
+ config: &config,
+ props: &revision_props,
+ testpaths,
+ revision: Some(revision)
+ };
+ rev_cx.run_revision();
+ }
+ }
+
+ base_cx.complete_all();
+
+ File::create(::stamp(&config, testpaths)).unwrap();
+}
+
+struct TestCx<'test> {
+ config: &'test Config,
+ props: &'test TestProps,
+ testpaths: &'test TestPaths,
+ revision: Option<&'test str>
+}
+
+struct DebuggerCommands {
+ commands: Vec<String>,
+ check_lines: Vec<String>,
+ breakpoint_lines: Vec<usize>,
+}
+
+impl<'test> TestCx<'test> {
+ /// invoked once before any revisions have been processed
+ fn init_all(&self) {
+ assert!(self.revision.is_none(), "init_all invoked for a revision");
+ if let Incremental = self.config.mode {
+ self.init_incremental_test()
+ }
+ }
+
+ /// Code executed for each revision in turn (or, if there are no
+ /// revisions, exactly once, with revision == None).
+ fn run_revision(&self) {
+ match self.config.mode {
+ CompileFail |
+ ParseFail => self.run_cfail_test(),
+ RunFail => self.run_rfail_test(),
+ RunPass => self.run_rpass_test(),
+ RunPassValgrind => self.run_valgrind_test(),
+ Pretty => self.run_pretty_test(),
+ DebugInfoGdb => self.run_debuginfo_gdb_test(),
+ DebugInfoLldb => self.run_debuginfo_lldb_test(),
+ Codegen => self.run_codegen_test(),
+ Rustdoc => self.run_rustdoc_test(),
+ CodegenUnits => self.run_codegen_units_test(),
+ Incremental => self.run_incremental_test(),
+ RunMake => self.run_rmake_test(),
+ Ui => self.run_ui_test(),
+ MirOpt => self.run_mir_opt_test(),
+ Assembly => self.run_assembly_test(),
+ }
+ }
+
+ /// Invoked after all revisions have executed.
+ fn complete_all(&self) {
+ assert!(self.revision.is_none(), "init_all invoked for a revision");
+ }
+
+ fn run_cfail_test(&self) {
+ let proc_res = self.compile_test();
+
+ if self.props.must_compile_successfully {
+ if !proc_res.status.success() {
+ self.fatal_proc_rec(
+ "test compilation failed although it shouldn't!",
+ &proc_res);
+ }
+ } else {
+ if proc_res.status.success() {
+ self.fatal_proc_rec(
+ &format!("{} test compiled successfully!", self.config.mode)[..],
+ &proc_res);
+ }
+
+ self.check_correct_failure_status(&proc_res);
+ }
+
+ 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() {
+ if !self.props.error_patterns.is_empty() {
+ self.fatal("both error pattern and expected errors specified");
+ }
+ self.check_expected_errors(expected_errors, &proc_res);
+ } else {
+ self.check_error_patterns(&output_to_check, &proc_res);
+ }
+
+ self.check_no_compiler_crash(&proc_res);
+ self.check_forbid_output(&output_to_check, &proc_res);
+ }
+
+ fn run_rfail_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();
+
+ // The value our Makefile configures valgrind to return on failure
+ const VALGRIND_ERR: i32 = 100;
+ if proc_res.status.code() == Some(VALGRIND_ERR) {
+ self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
+ }
+
+ let output_to_check = self.get_output(&proc_res);
+ self.check_correct_failure_status(&proc_res);
+ self.check_error_patterns(&output_to_check, &proc_res);
+ }
+
+ fn get_output(&self, proc_res: &ProcRes) -> String {
+ if self.props.check_stdout {
+ format!("{}{}", proc_res.stdout, proc_res.stderr)
+ } else {
+ proc_res.stderr.clone()
+ }
+ }
+
+ fn check_correct_failure_status(&self, proc_res: &ProcRes) {
+ // The value the rust runtime returns on failure
+ const RUST_ERR: i32 = 1;
+ if proc_res.status.code() != Some(RUST_ERR) {
+ self.fatal_proc_rec(
+ &format!("failure produced the wrong error: {}",
+ proc_res.status),
+ proc_res);
+ }
+ }
+
+ fn run_rpass_test(&self) {
+ let proc_res = self.compile_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("compilation failed!", &proc_res);
+ }
+
+ // FIXME(#41968): Move this check to tidy?
+ let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
+ assert!(expected_errors.is_empty(),
+ "run-pass tests with expected warnings should be moved to ui/");
+
+ let proc_res = self.exec_compiled_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("test run failed!", &proc_res);
+ }
+ }
+
+ fn run_valgrind_test(&self) {
+ assert!(self.revision.is_none(), "revisions not relevant here");
+
+ if self.config.valgrind_path.is_none() {
+ assert!(!self.config.force_valgrind);
+ return self.run_rpass_test();
+ }
+
+ let mut proc_res = self.compile_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("compilation failed!", &proc_res);
+ }
+
+ let mut new_config = self.config.clone();
+ new_config.runtool = new_config.valgrind_path.clone();
+ let new_cx = TestCx { config: &new_config, ..*self };
+ proc_res = new_cx.exec_compiled_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("test run failed!", &proc_res);
+ }
+ }
+
+ fn run_pretty_test(&self) {
+ if self.props.pp_exact.is_some() {
+ logv(self.config, "testing for exact pretty-printing".to_owned());
+ } else {
+ logv(self.config, "testing for converging pretty-printing".to_owned());
+ }
+
+ let rounds = match self.props.pp_exact { Some(_) => 1, None => 2 };
+
+ let mut src = String::new();
+ File::open(&self.testpaths.file).unwrap().read_to_string(&mut src).unwrap();
+ let mut srcs = vec![src];
+
+ let mut round = 0;
+ while round < rounds {
+ logv(self.config, format!("pretty-printing round {} revision {:?}",
+ round, self.revision));
+ let proc_res = self.print_source(srcs[round].to_owned(), &self.props.pretty_mode);
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec(&format!("pretty-printing failed in round {} revision {:?}",
+ round, self.revision),
+ &proc_res);
+ }
+
+ let ProcRes{ stdout, .. } = proc_res;
+ srcs.push(stdout);
+ round += 1;
+ }
+
+ let mut expected = match self.props.pp_exact {
+ Some(ref file) => {
+ let filepath = self.testpaths.file.parent().unwrap().join(file);
+ let mut s = String::new();
+ File::open(&filepath).unwrap().read_to_string(&mut s).unwrap();
+ s
+ }
+ None => { srcs[srcs.len() - 2].clone() }
+ };
+ let mut actual = srcs[srcs.len() - 1].clone();
+
+ if self.props.pp_exact.is_some() {
+ // Now we have to care about line endings
+ let cr = "\r".to_owned();
+ actual = actual.replace(&cr, "").to_owned();
+ expected = expected.replace(&cr, "").to_owned();
+ }
+
+ self.compare_source(&expected, &actual);
+
+ // If we're only making sure that the output matches then just stop here
+ if self.props.pretty_compare_only { return; }
+
+ // Finally, let's make sure it actually appears to remain valid code
+ let proc_res = self.typecheck_source(actual);
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
+ }
+
+ if !self.props.pretty_expanded { return }
+
+ // additionally, run `--pretty expanded` and try to build it.
+ let proc_res = self.print_source(srcs[round].clone(), "expanded");
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
+ }
+
+ let ProcRes{ stdout: expanded_src, .. } = proc_res;
+ let proc_res = self.typecheck_source(expanded_src);
+ if !proc_res.status.success() {
+ self.fatal_proc_rec(
+ "pretty-printed source (expanded) does not typecheck",
+ &proc_res);
+ }
+ }
+
+ fn print_source(&self, src: String, pretty_type: &str) -> ProcRes {
+ let aux_dir = self.aux_output_dir_name();
+
+ let mut rustc = Command::new(&self.config.rustc_path);
+ rustc.arg("-")
+ .args(&["-Z", &format!("unpretty={}", pretty_type)])
+ .args(&["--target", &self.config.target])
+ .arg("-L").arg(&aux_dir)
+ .args(self.split_maybe_args(&self.config.target_rustcflags))
+ .args(&self.props.compile_flags)
+ .envs(self.props.exec_env.clone());
+
+ self.compose_and_run(rustc,
+ self.config.compile_lib_path.to_str().unwrap(),
+ Some(aux_dir.to_str().unwrap()),
+ Some(src))
+ }
+
+ fn compare_source(&self,
+ expected: &str,
+ actual: &str) {
+ if expected != actual {
+ self.error("pretty-printed source does not match expected source");
+ println!("\n\
+expected:\n\
+------------------------------------------\n\
+{}\n\
+------------------------------------------\n\
+actual:\n\
+------------------------------------------\n\
+{}\n\
+------------------------------------------\n\
+\n",
+ expected, actual);
+ panic!();
+ }
+ }
+
+ fn typecheck_source(&self, src: String) -> ProcRes {
+ let mut rustc = Command::new(&self.config.rustc_path);
+
+ let out_dir = self.output_base_name().with_extension("pretty-out");
+ let _ = fs::remove_dir_all(&out_dir);
+ create_dir_all(&out_dir).unwrap();
+
+ let target = if self.props.force_host {
+ &*self.config.host
+ } else {
+ &*self.config.target
+ };
+
+ let aux_dir = self.aux_output_dir_name();
+
+ rustc.arg("-")
+ .arg("-Zno-trans")
+ .arg("--out-dir").arg(&out_dir)
+ .arg(&format!("--target={}", target))
+ .arg("-L").arg(&self.config.build_base)
+ .arg("-L").arg(aux_dir);
+
+ if let Some(revision) = self.revision {
+ rustc.args(&["--cfg", revision]);
+ }
+
+ rustc.args(self.split_maybe_args(&self.config.target_rustcflags));
+ rustc.args(&self.props.compile_flags);
+
+ self.compose_and_run_compiler(rustc, Some(src))
+ }
+
+ fn run_debuginfo_gdb_test(&self) {
+ assert!(self.revision.is_none(), "revisions not relevant here");
+
+ let config = Config {
+ target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
+ host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
+ .. self.config.clone()
+ };
+
+ let test_cx = TestCx {
+ config: &config,
+ ..*self
+ };
+
+ test_cx.run_debuginfo_gdb_test_no_opt();
+ }
+
+ fn run_debuginfo_gdb_test_no_opt(&self) {
+ let prefixes = if self.config.gdb_native_rust {
+ // GDB with Rust
+ static PREFIXES: &'static [&'static str] = &["gdb", "gdbr"];
+ println!("NOTE: compiletest thinks it is using GDB with native rust support");
+ PREFIXES
+ } else {
+ // Generic GDB
+ static PREFIXES: &'static [&'static str] = &["gdb", "gdbg"];
+ println!("NOTE: compiletest thinks it is using GDB without native rust support");
+ PREFIXES
+ };
+
+ let DebuggerCommands {
+ commands,
+ check_lines,
+ breakpoint_lines
+ } = self.parse_debugger_commands(prefixes);
+ let mut cmds = commands.join("\n");
+
+ // compile test file (it should have 'compile-flags:-g' in the header)
+ let compiler_run_result = self.compile_test();
+ if !compiler_run_result.status.success() {
+ self.fatal_proc_rec("compilation failed!", &compiler_run_result);
+ }
+
+ let exe_file = self.make_exe_name();
+
+ let debugger_run_result;
+ match &*self.config.target {
+ "arm-linux-androideabi" |
+ "armv7-linux-androideabi" |
+ "aarch64-linux-android" => {
+
+ cmds = cmds.replace("run", "continue");
+
+ let tool_path = match self.config.android_cross_path.to_str() {
+ Some(x) => x.to_owned(),
+ None => self.fatal("cannot find android cross path")
+ };
+
+ // write debugger script
+ let mut script_str = String::with_capacity(2048);
+ script_str.push_str(&format!("set charset {}\n", Self::charset()));
+ script_str.push_str(&format!("set sysroot {}\n", tool_path));
+ script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
+ script_str.push_str("target remote :5039\n");
+ script_str.push_str(&format!("set solib-search-path \
+ ./{}/stage2/lib/rustlib/{}/lib/\n",
+ self.config.host, self.config.target));
+ for line in &breakpoint_lines {
+ script_str.push_str(&format!("break {:?}:{}\n",
+ self.testpaths.file.file_name()
+ .unwrap()
+ .to_string_lossy(),
+ *line)[..]);
+ }
+ script_str.push_str(&cmds);
+ script_str.push_str("\nquit\n");
+
+ debug!("script_str = {}", script_str);
+ self.dump_output_file(&script_str, "debugger.script");
+
+ let adb_path = &self.config.adb_path;
+
+ Command::new(adb_path)
+ .arg("push")
+ .arg(&exe_file)
+ .arg(&self.config.adb_test_dir)
+ .status()
+ .expect(&format!("failed to exec `{:?}`", adb_path));
+
+ Command::new(adb_path)
+ .args(&["forward", "tcp:5039", "tcp:5039"])
+ .status()
+ .expect(&format!("failed to exec `{:?}`", adb_path));
+
+ let adb_arg = format!("export LD_LIBRARY_PATH={}; \
+ gdbserver{} :5039 {}/{}",
+ self.config.adb_test_dir.clone(),
+ if self.config.target.contains("aarch64")
+ {"64"} else {""},
+ self.config.adb_test_dir.clone(),
+ exe_file.file_name().unwrap().to_str()
+ .unwrap());
+
+ debug!("adb arg: {}", adb_arg);
+ let mut adb = Command::new(adb_path)
+ .args(&["shell", &adb_arg])
+ .stdout(Stdio::piped())
+ .stderr(Stdio::inherit())
+ .spawn()
+ .expect(&format!("failed to exec `{:?}`", adb_path));
+
+ // Wait for the gdbserver to print out "Listening on port ..."
+ // at which point we know that it's started and then we can
+ // execute the debugger below.
+ let mut stdout = BufReader::new(adb.stdout.take().unwrap());
+ let mut line = String::new();
+ loop {
+ line.truncate(0);
+ stdout.read_line(&mut line).unwrap();
+ if line.starts_with("Listening on port 5039") {
+ break
+ }
+ }
+ drop(stdout);
+
+ let debugger_script = self.make_out_name("debugger.script");
+ // FIXME (#9639): This needs to handle non-utf8 paths
+ let debugger_opts =
+ vec!["-quiet".to_owned(),
+ "-batch".to_owned(),
+ "-nx".to_owned(),
+ format!("-command={}", debugger_script.to_str().unwrap())];
+
+ let mut gdb_path = tool_path;
+ gdb_path.push_str("/bin/gdb");
+ let Output {
+ status,
+ stdout,
+ stderr
+ } = Command::new(&gdb_path)
+ .args(&debugger_opts)
+ .output()
+ .expect(&format!("failed to exec `{:?}`", gdb_path));
+ let cmdline = {
+ let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
+ gdb.args(&debugger_opts);
+ let cmdline = self.make_cmdline(&gdb, "");
+ logv(self.config, format!("executing {}", cmdline));
+ cmdline
+ };
+
+ debugger_run_result = ProcRes {
+ status,
+ stdout: String::from_utf8(stdout).unwrap(),
+ stderr: String::from_utf8(stderr).unwrap(),
+ cmdline,
+ };
+ if adb.kill().is_err() {
+ println!("Adb process is already finished.");
+ }
+ }
+
+ _ => {
+ let rust_src_root = self.config.find_rust_src_root().expect(
+ "Could not find Rust source root",
+ );
+ let rust_pp_module_rel_path = Path::new("./src/etc");
+ let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
+ .to_str()
+ .unwrap()
+ .to_owned();
+ // write debugger script
+ let mut script_str = String::with_capacity(2048);
+ script_str.push_str(&format!("set charset {}\n", Self::charset()));
+ script_str.push_str("show version\n");
+
+ match self.config.gdb_version {
+ Some(version) => {
+ println!("NOTE: compiletest thinks it is using GDB version {}",
+ version);
+
+ if version > extract_gdb_version("7.4").unwrap() {
+ // Add the directory containing the pretty printers to
+ // GDB's script auto loading safe path
+ script_str.push_str(
+ &format!("add-auto-load-safe-path {}\n",
+ rust_pp_module_abs_path.replace(r"\", r"\\"))
+ );
+ }
+ }
+ _ => {
+ println!("NOTE: compiletest does not know which version of \
+ GDB it is using");
+ }
+ }
+
+ // The following line actually doesn't have to do anything with
+ // pretty printing, it just tells GDB to print values on one line:
+ script_str.push_str("set print pretty off\n");
+
+ // Add the pretty printer directory to GDB's source-file search path
+ script_str.push_str(&format!("directory {}\n",
+ rust_pp_module_abs_path));
+
+ // Load the target executable
+ script_str.push_str(&format!("file {}\n",
+ exe_file.to_str().unwrap()
+ .replace(r"\", r"\\")));
+
+ // Force GDB to print values in the Rust format.
+ if self.config.gdb_native_rust {
+ script_str.push_str("set language rust\n");
+ }
+
+ // Add line breakpoints
+ for line in &breakpoint_lines {
+ script_str.push_str(&format!("break '{}':{}\n",
+ self.testpaths.file.file_name().unwrap()
+ .to_string_lossy(),
+ *line));
+ }
+
+ script_str.push_str(&cmds);
+ script_str.push_str("\nquit\n");
+
+ debug!("script_str = {}", script_str);
+ self.dump_output_file(&script_str, "debugger.script");
+
+ let debugger_script = self.make_out_name("debugger.script");
+
+ // FIXME (#9639): This needs to handle non-utf8 paths
+ let debugger_opts =
+ vec!["-quiet".to_owned(),
+ "-batch".to_owned(),
+ "-nx".to_owned(),
+ format!("-command={}", debugger_script.to_str().unwrap())];
+
+ let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
+ gdb.args(&debugger_opts)
+ .env("PYTHONPATH", rust_pp_module_abs_path);
+
+ debugger_run_result =
+ self.compose_and_run(gdb,
+ self.config.run_lib_path.to_str().unwrap(),
+ None,
+ None);
+ }
+ }
+
+ if !debugger_run_result.status.success() {
+ self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
+ }
+
+ self.check_debugger_output(&debugger_run_result, &check_lines);
+ }
+
+ fn run_debuginfo_lldb_test(&self) {
+ assert!(self.revision.is_none(), "revisions not relevant here");
+
+ if self.config.lldb_python_dir.is_none() {
+ self.fatal("Can't run LLDB test because LLDB's python path is not set.");
+ }
+
+ let config = Config {
+ target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
+ host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
+ .. self.config.clone()
+ };
+
+
+ let test_cx = TestCx {
+ config: &config,
+ ..*self
+ };
+
+ test_cx.run_debuginfo_lldb_test_no_opt();
+ }
+
+ fn run_debuginfo_lldb_test_no_opt(&self) {
+ // compile test file (it should have 'compile-flags:-g' in the header)
+ let compile_result = self.compile_test();
+ if !compile_result.status.success() {
+ self.fatal_proc_rec("compilation failed!", &compile_result);
+ }
+
+ let exe_file = self.make_exe_name();
+
+ match self.config.lldb_version {
+ Some(ref version) => {
+ println!("NOTE: compiletest thinks it is using LLDB version {}",
+ version);
+ }
+ _ => {
+ println!("NOTE: compiletest does not know which version of \
+ LLDB it is using");
+ }
+ }
+
+ // Parse debugger commands etc from test files
+ let DebuggerCommands {
+ commands,
+ check_lines,
+ breakpoint_lines,
+ ..
+ } = self.parse_debugger_commands(&["lldb"]);
+
+ // Write debugger script:
+ // We don't want to hang when calling `quit` while the process is still running
+ let mut script_str = String::from("settings set auto-confirm true\n");
+
+ // Make LLDB emit its version, so we have it documented in the test output
+ script_str.push_str("version\n");
+
+ // Switch LLDB into "Rust mode"
+ let rust_src_root = self.config.find_rust_src_root().expect(
+ "Could not find Rust source root",
+ );
+ let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py");
+ let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
+ .to_str()
+ .unwrap()
+ .to_owned();
+
+ script_str.push_str(&format!("command script import {}\n",
+ &rust_pp_module_abs_path[..])[..]);
+ script_str.push_str("type summary add --no-value ");
+ script_str.push_str("--python-function lldb_rust_formatters.print_val ");
+ script_str.push_str("-x \".*\" --category Rust\n");
+ script_str.push_str("type category enable Rust\n");
+
+ // Set breakpoints on every line that contains the string "#break"
+ let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
+ for line in &breakpoint_lines {
+ script_str.push_str(&format!("breakpoint set --file '{}' --line {}\n",
+ source_file_name,
+ line));
+ }
+
+ // Append the other commands
+ for line in &commands {
+ script_str.push_str(line);
+ script_str.push_str("\n");
+ }
+
+ // Finally, quit the debugger
+ script_str.push_str("\nquit\n");
+
+ // Write the script into a file
+ debug!("script_str = {}", script_str);
+ self.dump_output_file(&script_str, "debugger.script");
+ let debugger_script = self.make_out_name("debugger.script");
+
+ // Let LLDB execute the script via lldb_batchmode.py
+ let debugger_run_result = self.run_lldb(&exe_file,
+ &debugger_script,
+ &rust_src_root);
+
+ if !debugger_run_result.status.success() {
+ self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
+ }
+
+ self.check_debugger_output(&debugger_run_result, &check_lines);
+ }
+
+ fn run_lldb(&self,
+ test_executable: &Path,
+ debugger_script: &Path,
+ rust_src_root: &Path)
+ -> ProcRes {
+ // Prepare the lldb_batchmode which executes the debugger script
+ let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
+ self.cmd2procres(Command::new(&self.config.lldb_python)
+ .arg(&lldb_script_path)
+ .arg(test_executable)
+ .arg(debugger_script)
+ .env("PYTHONPATH",
+ self.config.lldb_python_dir.as_ref().unwrap()))
+ }
+
+ fn cmd2procres(&self, cmd: &mut Command) -> ProcRes {
+ let (status, out, err) = match cmd.output() {
+ Ok(Output { status, stdout, stderr }) => {
+ (status,
+ String::from_utf8(stdout).unwrap(),
+ String::from_utf8(stderr).unwrap())
+ },
+ Err(e) => {
+ self.fatal(&format!("Failed to setup Python process for \
+ LLDB script: {}", e))
+ }
+ };
+
+ self.dump_output(&out, &err);
+ ProcRes {
+ status,
+ stdout: out,
+ stderr: err,
+ cmdline: format!("{:?}", cmd)
+ }
+ }
+
+ fn parse_debugger_commands(&self, debugger_prefixes: &[&str]) -> DebuggerCommands {
+ let directives = debugger_prefixes.iter().map(|prefix| (
+ format!("{}-command", prefix),
+ format!("{}-check", prefix),
+ )).collect::<Vec<_>>();
+
+ let mut breakpoint_lines = vec![];
+ let mut commands = vec![];
+ let mut check_lines = vec![];
+ let mut counter = 1;
+ let reader = BufReader::new(File::open(&self.testpaths.file).unwrap());
+ for line in reader.lines() {
+ match line {
+ Ok(line) => {
+ if line.contains("#break") {
+ breakpoint_lines.push(counter);
+ }
+
+ for &(ref command_directive, ref check_directive) in &directives {
+ self.config.parse_name_value_directive(
+ &line,
+ command_directive).map(|cmd| {
+ commands.push(cmd)
+ });
+
+ self.config.parse_name_value_directive(
+ &line,
+ check_directive).map(|cmd| {
+ check_lines.push(cmd)
+ });
+ }
+ }
+ Err(e) => {
+ self.fatal(&format!("Error while parsing debugger commands: {}", e))
+ }
+ }
+ counter += 1;
+ }
+
+ DebuggerCommands {
+ commands,
+ check_lines,
+ breakpoint_lines,
+ }
+ }
+
+ fn cleanup_debug_info_options(&self, options: &Option<String>) -> Option<String> {
+ if options.is_none() {
+ return None;
+ }
+
+ // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
+ let options_to_remove = [
+ "-O".to_owned(),
+ "-g".to_owned(),
+ "--debuginfo".to_owned()
+ ];
+ let new_options =
+ self.split_maybe_args(options).into_iter()
+ .filter(|x| !options_to_remove.contains(x))
+ .collect::<Vec<String>>();
+
+ Some(new_options.join(" "))
+ }
+
+ fn check_debugger_output(&self, debugger_run_result: &ProcRes, check_lines: &[String]) {
+ let num_check_lines = check_lines.len();
+
+ let mut check_line_index = 0;
+ for line in debugger_run_result.stdout.lines() {
+ if check_line_index >= num_check_lines {
+ break;
+ }
+
+ if check_single_line(line, &(check_lines[check_line_index])[..]) {
+ check_line_index += 1;
+ }
+ }
+ if check_line_index != num_check_lines && num_check_lines > 0 {
+ self.fatal_proc_rec(&format!("line not found in debugger output: {}",
+ check_lines[check_line_index]),
+ debugger_run_result);
+ }
+
+ fn check_single_line(line: &str, check_line: &str) -> bool {
+ // Allow check lines to leave parts unspecified (e.g., uninitialized
+ // bits in the wrong case of an enum) with the notation "[...]".
+ let line = line.trim();
+ let check_line = check_line.trim();
+ let can_start_anywhere = check_line.starts_with("[...]");
+ let can_end_anywhere = check_line.ends_with("[...]");
+
+ let check_fragments: Vec<&str> = check_line.split("[...]")
+ .filter(|frag| !frag.is_empty())
+ .collect();
+ if check_fragments.is_empty() {
+ return true;
+ }
+
+ let (mut rest, first_fragment) = if can_start_anywhere {
+ match line.find(check_fragments[0]) {
+ Some(pos) => (&line[pos + check_fragments[0].len() ..], 1),
+ None => return false
+ }
+ } else {
+ (line, 0)
+ };
+
+ for current_fragment in &check_fragments[first_fragment..] {
+ match rest.find(current_fragment) {
+ Some(pos) => {
+ rest = &rest[pos + current_fragment.len() .. ];
+ }
+ None => return false
+ }
+ }
+
+ if !can_end_anywhere && !rest.is_empty() {
+ return false;
+ }
+
+ true
+ }
+ }
+
+ fn check_error_patterns(&self,
+ output_to_check: &str,
+ proc_res: &ProcRes) {
+ if self.props.error_patterns.is_empty() {
+ if self.props.must_compile_successfully {
+ return
+ } else {
+ self.fatal(&format!("no error pattern specified in {:?}",
+ self.testpaths.file.display()));
+ }
+ }
+ let mut next_err_idx = 0;
+ let mut next_err_pat = self.props.error_patterns[next_err_idx].trim();
+ let mut done = false;
+ for line in output_to_check.lines() {
+ if line.contains(next_err_pat) {
+ debug!("found error pattern {}", next_err_pat);
+ next_err_idx += 1;
+ if next_err_idx == self.props.error_patterns.len() {
+ debug!("found all error patterns");
+ done = true;
+ break;
+ }
+ next_err_pat = self.props.error_patterns[next_err_idx].trim();
+ }
+ }
+ if done { return; }
+
+ let missing_patterns = &self.props.error_patterns[next_err_idx..];
+ if missing_patterns.len() == 1 {
+ self.fatal_proc_rec(
+ &format!("error pattern '{}' not found!", missing_patterns[0]),
+ proc_res);
+ } else {
+ for pattern in missing_patterns {
+ self.error(&format!("error pattern '{}' not found!", *pattern));
+ }
+ self.fatal_proc_rec("multiple error patterns not found", proc_res);
+ }
+ }
+
+ fn check_no_compiler_crash(&self, proc_res: &ProcRes) {
+ for line in proc_res.stderr.lines() {
+ if line.contains("error: internal compiler error") {
+ self.fatal_proc_rec("compiler encountered internal error", proc_res);
+ }
+ }
+ }
+
+ fn check_forbid_output(&self,
+ output_to_check: &str,
+ proc_res: &ProcRes) {
+ for pat in &self.props.forbid_output {
+ if output_to_check.contains(pat) {
+ self.fatal_proc_rec("forbidden pattern found in compiler output", proc_res);
+ }
+ }
+ }
+
+ fn check_expected_errors(&self,
+ expected_errors: Vec<errors::Error>,
+ proc_res: &ProcRes) {
+ 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);
+ }
+
+ // On Windows, keep all '\' path separators to match the paths reported in the JSON output
+ // from the compiler
+ let os_file_name = self.testpaths.file.display().to_string();
+
+ let file_name =
+ format!("{}", self.testpaths.file.display())
+ .replace(r"\", "/"); // on windows, translate all '\' path separators to '/'
+
+ // If the testcase being checked contains at least one expected "help"
+ // message, then we'll ensure that all "help" messages are expected.
+ // Otherwise, all "help" messages reported by the compiler will be ignored.
+ // This logic also applies to "note" messages.
+ let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help));
+ let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
+
+ // Parse the JSON output from the compiler and extract out the messages.
+ let actual_errors = json::parse_output(&os_file_name, &proc_res.stderr, proc_res);
+ let mut unexpected = Vec::new();
+ let mut found = vec![false; expected_errors.len()];
+ for actual_error in &actual_errors {
+ let opt_index =
+ expected_errors
+ .iter()
+ .enumerate()
+ .position(|(index, expected_error)| {
+ !found[index] &&
+ actual_error.line_num == expected_error.line_num &&
+ (expected_error.kind.is_none() ||
+ actual_error.kind == expected_error.kind) &&
+ actual_error.msg.contains(&expected_error.msg)
+ });
+
+ match opt_index {
+ Some(index) => {
+ // found a match, everybody is happy
+ assert!(!found[index]);
+ found[index] = true;
+ }
+
+ None => {
+ if self.is_unexpected_compiler_message(actual_error, expect_help, expect_note) {
+ self.error(
+ &format!("{}:{}: unexpected {}: '{}'",
+ file_name,
+ actual_error.line_num,
+ actual_error.kind.as_ref()
+ .map_or(String::from("message"),
+ |k| k.to_string()),
+ actual_error.msg));
+ unexpected.push(actual_error);
+ }
+ }
+ }
+ }
+
+ 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] {
+ self.error(
+ &format!("{}:{}: expected {} not found: {}",
+ file_name,
+ expected_error.line_num,
+ expected_error.kind.as_ref()
+ .map_or("message".into(),
+ |k| k.to_string()),
+ expected_error.msg));
+ not_found.push(expected_error);
+ }
+ }
+
+ if !unexpected.is_empty() || !not_found.is_empty() {
+ self.error(
+ &format!("{} unexpected errors found, {} expected errors not found",
+ unexpected.len(), not_found.len()));
+ println!("status: {}\ncommand: {}",
+ proc_res.status, proc_res.cmdline);
+ if !unexpected.is_empty() {
+ println!("unexpected errors (from JSON output): {:#?}\n", unexpected);
+ }
+ if !not_found.is_empty() {
+ println!("not found errors (from test file): {:#?}\n", not_found);
+ }
+ panic!();
+ }
+ }
+
+ /// Returns true if we should report an error about `actual_error`,
+ /// which did not match any of the expected error. We always require
+ /// errors/warnings to be explicitly listed, but only require
+ /// helps/notes if there are explicit helps/notes given.
+ fn is_unexpected_compiler_message(&self,
+ actual_error: &Error,
+ expect_help: bool,
+ expect_note: bool)
+ -> bool {
+ match actual_error.kind {
+ Some(ErrorKind::Help) => expect_help,
+ Some(ErrorKind::Note) => expect_note,
+ Some(ErrorKind::Error) |
+ Some(ErrorKind::Warning) => true,
+ Some(ErrorKind::Suggestion) |
+ None => false
+ }
+ }
+
+ fn compile_test(&self) -> ProcRes {
+ let allow_unused = match self.config.mode {
+ CompileFail | Ui => {
+ // compile-fail and ui tests tend to have tons of unused code as
+ // it's just testing various pieces of the compile, but we don't
+ // want to actually assert warnings about all this code. Instead
+ // let's just ignore unused code warnings by defaults and tests
+ // can turn it back on if needed.
+ AllowUnused::Yes
+ }
+ _ => AllowUnused::No
+ };
+
+ let mut rustc = self.make_compile_args(
+ &self.testpaths.file, TargetLocation::ThisFile(self.make_exe_name()), allow_unused);
+
+ rustc.arg("-L").arg(&self.aux_output_dir_name());
+
+ self.compose_and_run_compiler(rustc, None)
+ }
+
+ fn document(&self, out_dir: &Path) -> ProcRes {
+ if self.props.build_aux_docs {
+ for rel_ab in &self.props.aux_builds {
+ let aux_testpaths = self.compute_aux_test_paths(rel_ab);
+ let aux_props = self.props.from_aux_file(&aux_testpaths.file,
+ self.revision,
+ self.config);
+ let aux_cx = TestCx {
+ config: self.config,
+ props: &aux_props,
+ testpaths: &aux_testpaths,
+ revision: self.revision
+ };
+ let auxres = aux_cx.document(out_dir);
+ if !auxres.status.success() {
+ return auxres;
+ }
+ }
+ }
+
+ let aux_dir = self.aux_output_dir_name();
+
+ let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path passed");
+ let mut rustdoc = Command::new(rustdoc_path);
+
+ rustdoc.arg("-L").arg(aux_dir)
+ .arg("-o").arg(out_dir)
+ .arg(&self.testpaths.file)
+ .args(&self.props.compile_flags);
+ if let Some(ref linker) = self.config.linker {
+ rustdoc.arg("--linker").arg(linker).arg("-Z").arg("unstable-options");
+ }
+
+ self.compose_and_run_compiler(rustdoc, None)
+ }
+
+ fn exec_compiled_test(&self) -> ProcRes {
+ let env = &self.props.exec_env;
+
+ match &*self.config.target {
+ // This is pretty similar to below, we're transforming:
+ //
+ // program arg1 arg2
+ //
+ // into
+ //
+ // remote-test-client run program:support-lib.so arg1 arg2
+ //
+ // The test-client program will upload `program` to the emulator
+ // along with all other support libraries listed (in this case
+ // `support-lib.so`. It will then execute the program on the
+ // emulator with the arguments specified (in the environment we give
+ // the process) and then report back the same result.
+ _ if self.config.remote_test_client.is_some() => {
+ let aux_dir = self.aux_output_dir_name();
+ let ProcArgs { mut prog, args } = self.make_run_args();
+ if let Ok(entries) = aux_dir.read_dir() {
+ for entry in entries {
+ let entry = entry.unwrap();
+ if !entry.path().is_file() {
+ continue
+ }
+ prog.push_str(":");
+ prog.push_str(entry.path().to_str().unwrap());
+ }
+ }
+ let mut test_client = Command::new(
+ self.config.remote_test_client.as_ref().unwrap());
+ test_client
+ .args(&["run", &prog])
+ .args(args)
+ .envs(env.clone());
+ self.compose_and_run(test_client,
+ self.config.run_lib_path.to_str().unwrap(),
+ Some(aux_dir.to_str().unwrap()),
+ None)
+ }
+ _ => {
+ let aux_dir = self.aux_output_dir_name();
+ let ProcArgs { prog, args } = self.make_run_args();
+ let mut program = Command::new(&prog);
+ program.args(args)
+ .current_dir(&self.output_base_name().parent().unwrap())
+ .envs(env.clone());
+ self.compose_and_run(program,
+ self.config.run_lib_path.to_str().unwrap(),
+ Some(aux_dir.to_str().unwrap()),
+ None)
+ }
+ }
+ }
+
+ /// For each `aux-build: foo/bar` annotation, we check to find the
+ /// file in a `aux` directory relative to the test itself.
+ fn compute_aux_test_paths(&self, rel_ab: &str) -> TestPaths {
+ let test_ab = self.testpaths.file
+ .parent()
+ .expect("test file path has no parent")
+ .join("auxiliary")
+ .join(rel_ab);
+ if !test_ab.exists() {
+ self.fatal(&format!("aux-build `{}` source not found", test_ab.display()))
+ }
+
+ TestPaths {
+ file: test_ab,
+ base: self.testpaths.base.clone(),
+ relative_dir: self.testpaths.relative_dir
+ .join("auxiliary")
+ .join(rel_ab)
+ .parent()
+ .expect("aux-build path has no parent")
+ .to_path_buf()
+ }
+ }
+
+ fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
+ if !self.props.aux_builds.is_empty() {
+ create_dir_all(&self.aux_output_dir_name()).unwrap();
+ }
+
+ let aux_dir = self.aux_output_dir_name();
+
+ for rel_ab in &self.props.aux_builds {
+ let aux_testpaths = self.compute_aux_test_paths(rel_ab);
+ let aux_props = self.props.from_aux_file(&aux_testpaths.file,
+ self.revision,
+ self.config);
+ let aux_output = {
+ let f = self.make_lib_name(&self.testpaths.file);
+ let parent = f.parent().unwrap();
+ TargetLocation::ThisDirectory(parent.to_path_buf())
+ };
+ let aux_cx = TestCx {
+ config: self.config,
+ props: &aux_props,
+ testpaths: &aux_testpaths,
+ revision: self.revision
+ };
+ let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output, AllowUnused::No);
+
+ let crate_type = if aux_props.no_prefer_dynamic {
+ None
+ } else if (self.config.target.contains("musl") && !aux_props.force_host) ||
+ self.config.target.contains("wasm32") ||
+ self.config.target.contains("emscripten") {
+ // We primarily compile all auxiliary libraries as dynamic libraries
+ // to avoid code size bloat and large binaries as much as possible
+ // for the test suite (otherwise including libstd statically in all
+ // executables takes up quite a bit of space).
+ //
+ // For targets like MUSL or Emscripten, however, there is no support for
+ // dynamic libraries so we just go back to building a normal library. Note,
+ // however, that for MUSL if the library is built with `force_host` then
+ // it's ok to be a dylib as the host should always support dylibs.
+ Some("lib")
+ } else {
+ Some("dylib")
+ };
+
+ if let Some(crate_type) = crate_type {
+ aux_rustc.args(&["--crate-type", crate_type]);
+ }
+
+ aux_rustc.arg("-L").arg(&aux_dir);
+
+ let auxres = aux_cx.compose_and_run(aux_rustc,
+ aux_cx.config.compile_lib_path.to_str().unwrap(),
+ Some(aux_dir.to_str().unwrap()),
+ None);
+ if !auxres.status.success() {
+ self.fatal_proc_rec(
+ &format!("auxiliary build of {:?} failed to compile: ",
+ aux_testpaths.file.display()),
+ &auxres);
+ }
+ }
+
+ rustc.envs(self.props.rustc_env.clone());
+ self.compose_and_run(rustc,
+ self.config.compile_lib_path.to_str().unwrap(),
+ Some(aux_dir.to_str().unwrap()),
+ input)
+ }
+
+ fn compose_and_run(&self,
+ mut command: Command,
+ lib_path: &str,
+ aux_path: Option<&str>,
+ input: Option<String>) -> ProcRes {
+ let cmdline =
+ {
+ let cmdline = self.make_cmdline(&command, lib_path);
+ logv(self.config, format!("executing {}", cmdline));
+ cmdline
+ };
+
+ command
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .stdin(Stdio::piped());
+
+ // Need to be sure to put both the lib_path and the aux path in the dylib
+ // search path for the child.
+ let mut path = env::split_paths(&env::var_os(dylib_env_var()).unwrap_or(OsString::new()))
+ .collect::<Vec<_>>();
+ if let Some(p) = aux_path {
+ path.insert(0, PathBuf::from(p))
+ }
+ path.insert(0, PathBuf::from(lib_path));
+
+ // Add the new dylib search path var
+ let newpath = env::join_paths(&path).unwrap();
+ command.env(dylib_env_var(), newpath);
+
+ let mut child = command.spawn().expect(&format!("failed to exec `{:?}`", &command));
+ if let Some(input) = input {
+ child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
+ }
+
+ let Output { status, stdout, stderr } = read2_abbreviated(child)
+ .expect("failed to read output");
+
+ let result = ProcRes {
+ status,
+ stdout: String::from_utf8_lossy(&stdout).into_owned(),
+ stderr: String::from_utf8_lossy(&stderr).into_owned(),
+ cmdline,
+ };
+
+ self.dump_output(&result.stdout, &result.stderr);
+
+ result
+ }
+
+ fn make_compile_args(&self, input_file: &Path, output_file: TargetLocation, allow_unused: AllowUnused) -> Command {
+ let mut rustc = Command::new(&self.config.rustc_path);
+ rustc.arg(input_file)
+ .arg("-L").arg(&self.config.build_base);
+
+ // Optionally prevent default --target if specified in test compile-flags.
+ let custom_target = self.props.compile_flags
+ .iter()
+ .fold(false, |acc, x| acc || x.starts_with("--target"));
+
+ if !custom_target {
+ let target = if self.props.force_host {
+ &*self.config.host
+ } else {
+ &*self.config.target
+ };
+
+ rustc.arg(&format!("--target={}", target));
+ }
+
+ if let Some(revision) = self.revision {
+ rustc.args(&["--cfg", revision]);
+ }
+
+ if let Some(ref incremental_dir) = self.props.incremental_dir {
+ rustc.args(&["-Z", &format!("incremental={}", incremental_dir.display())]);
+ rustc.args(&["-Z", "incremental-verify-ich"]);
+ rustc.args(&["-Z", "incremental-queries"]);
+ }
+
+ match self.config.mode {
+ CompileFail |
+ ParseFail |
+ 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() {
+ rustc.args(&["--error-format", "json"]);
+ }
+ }
+ MirOpt => {
+ rustc.args(&[
+ "-Zdump-mir=all",
+ "-Zmir-opt-level=3",
+ "-Zdump-mir-exclude-pass-number"]);
+
+ let mir_dump_dir = self.get_mir_dump_dir();
+ let _ = fs::remove_dir_all(&mir_dump_dir);
+ create_dir_all(mir_dump_dir.as_path()).unwrap();
+ let mut dir_opt = "-Zdump-mir-dir=".to_string();
+ dir_opt.push_str(mir_dump_dir.to_str().unwrap());
+ debug!("dir_opt: {:?}", dir_opt);
+
+ rustc.arg(dir_opt);
+ }
+ RunPass | Ui => {
+ if !self
+ .props
+ .compile_flags
+ .iter()
+ .any(|s| s.starts_with("--error-format"))
+ {
+ rustc.args(&["--error-format", "json"]);
+ }
+ }
+ RunFail |
+ RunPassValgrind |
+ Pretty |
+ DebugInfoGdb |
+ DebugInfoLldb |
+ Codegen |
+ Rustdoc |
+ RunMake |
+ CodegenUnits |
+ Assembly => {
+ // do not use JSON output
+ }
+ }
+
+
+ if self.config.target == "wasm32-unknown-unknown" {
+ // rustc.arg("-g"); // get any backtrace at all on errors
+ } else if !self.props.no_prefer_dynamic {
+ rustc.args(&["-C", "prefer-dynamic"]);
+ }
+
+ match output_file {
+ TargetLocation::ThisFile(path) => {
+ rustc.arg("-o").arg(path);
+ }
+ TargetLocation::ThisDirectory(path) => {
+ rustc.arg("--out-dir").arg(path);
+ }
+ }
+
+ // Add `-A unused` before `config` flags and in-test (`props`) flags, so that they can
+ // overwrite this.
+ if let AllowUnused::Yes = allow_unused {
+ rustc.args(&["-A", "unused"]);
+ }
+
+ if self.props.force_host {
+ rustc.args(self.split_maybe_args(&self.config.host_rustcflags));
+ } else {
+ rustc.args(self.split_maybe_args(&self.config.target_rustcflags));
+ }
+ if let Some(ref linker) = self.config.linker {
+ rustc.arg(format!("-Clinker={}", linker));
+ }
+
+ rustc.args(&self.props.compile_flags);
+
+ rustc
+ }
+
+ fn make_lib_name(&self, auxfile: &Path) -> PathBuf {
+ // what we return here is not particularly important, as it
+ // happens; rustc ignores everything except for the directory.
+ let auxname = self.output_testname(auxfile);
+ self.aux_output_dir_name().join(&auxname)
+ }
+
+ fn make_exe_name(&self) -> PathBuf {
+ let mut f = self.output_base_name();
+ // FIXME: This is using the host architecture exe suffix, not target!
+ if self.config.target.contains("emscripten") {
+ let mut fname = f.file_name().unwrap().to_os_string();
+ fname.push(".js");
+ f.set_file_name(&fname);
+ } else if self.config.target.contains("spirv") {
+ let mut fname = f.file_name().unwrap().to_os_string();
+ fname.push(".spv");
+ f.set_file_name(&fname);
+ } else if self.config.target.contains("wasm32") {
+ let mut fname = f.file_name().unwrap().to_os_string();
+ fname.push(".wasm");
+ f.set_file_name(&fname);
+ } else if !env::consts::EXE_SUFFIX.is_empty() {
+ let mut fname = f.file_name().unwrap().to_os_string();
+ fname.push(env::consts::EXE_SUFFIX);
+ f.set_file_name(&fname);
+ }
+ f
+ }
+
+ fn make_run_args(&self) -> ProcArgs {
+ // If we've got another tool to run under (valgrind),
+ // then split apart its command
+ let mut args = self.split_maybe_args(&self.config.runtool);
+
+ // If this is emscripten, then run tests under nodejs
+ if self.config.target.contains("emscripten") {
+ if let Some(ref p) = self.config.nodejs {
+ args.push(p.clone());
+ } else {
+ self.fatal("no NodeJS binary found (--nodejs)");
+ }
+ }
+
+ // If this is otherwise wasm , then run tests under nodejs with our
+ // shim
+ if self.config.target.contains("wasm32") {
+ if let Some(ref p) = self.config.nodejs {
+ args.push(p.clone());
+ } else {
+ self.fatal("no NodeJS binary found (--nodejs)");
+ }
+
+ let src = self.config.src_base
+ .parent().unwrap() // chop off `run-pass`
+ .parent().unwrap() // chop off `test`
+ .parent().unwrap(); // chop off `src`
+ args.push(src.join("src/etc/wasm32-shim.js").display().to_string());
+ }
+
+ let exe_file = self.make_exe_name();
+
+ // FIXME (#9639): This needs to handle non-utf8 paths
+ args.push(exe_file.to_str().unwrap().to_owned());
+
+ // Add the arguments in the run_flags directive
+ args.extend(self.split_maybe_args(&self.props.run_flags));
+
+ let prog = args.remove(0);
+ ProcArgs {
+ prog,
+ args,
+ }
+ }
+
+ fn split_maybe_args(&self, argstr: &Option<String>) -> Vec<String> {
+ match *argstr {
+ Some(ref s) => {
+ s
+ .split(' ')
+ .filter_map(|s| {
+ if s.chars().all(|c| c.is_whitespace()) {
+ None
+ } else {
+ Some(s.to_owned())
+ }
+ }).collect()
+ }
+ None => Vec::new()
+ }
+ }
+
+ fn make_cmdline(&self, command: &Command, libpath: &str) -> String {
+ use util;
+
+ // Linux and mac don't require adjusting the library search path
+ if cfg!(unix) {
+ format!("{:?}", command)
+ } else {
+ // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
+ // for diagnostic purposes
+ fn lib_path_cmd_prefix(path: &str) -> String {
+ format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
+ }
+
+ format!("{} {:?}", lib_path_cmd_prefix(libpath), command)
+ }
+ }
+
+ fn dump_output(&self, out: &str, err: &str) {
+ let revision = if let Some(r) = self.revision {
+ format!("{}.", r)
+ } else {
+ String::new()
+ };
+
+ self.dump_output_file(out, &format!("{}out", revision));
+ self.dump_output_file(err, &format!("{}err", revision));
+ self.maybe_dump_to_stdout(out, err);
+ }
+
+ fn dump_output_file(&self,
+ out: &str,
+ extension: &str) {
+ let outfile = self.make_out_name(extension);
+ File::create(&outfile).unwrap().write_all(out.as_bytes()).unwrap();
+ }
+
+ fn make_out_name(&self, extension: &str) -> PathBuf {
+ self.output_base_name().with_extension(extension)
+ }
+
+ fn aux_output_dir_name(&self) -> PathBuf {
+ let f = self.output_base_name();
+ let mut fname = f.file_name().unwrap().to_os_string();
+ fname.push(&format!("{}.aux", self.config.mode.disambiguator()));
+ f.with_file_name(&fname)
+ }
+
+ fn output_testname(&self, filepath: &Path) -> PathBuf {
+ PathBuf::from(filepath.file_stem().unwrap())
+ }
+
+ /// Given a test path like `compile-fail/foo/bar.rs` Returns a name like
+ /// `<output>/foo/bar-stage1`
+ fn output_base_name(&self) -> PathBuf {
+ let dir = self.config.build_base.join(&self.testpaths.relative_dir);
+
+ // Note: The directory `dir` is created during `collect_tests_from_dir`
+ dir
+ .join(&self.output_testname(&self.testpaths.file))
+ .with_extension(&self.config.stage_id)
+ }
+
+ fn maybe_dump_to_stdout(&self, out: &str, err: &str) {
+ if self.config.verbose {
+ println!("------{}------------------------------", "stdout");
+ println!("{}", out);
+ println!("------{}------------------------------", "stderr");
+ println!("{}", err);
+ println!("------------------------------------------");
+ }
+ }
+
+ fn error(&self, err: &str) {
+ match self.revision {
+ Some(rev) => println!("\nerror in revision `{}`: {}", rev, err),
+ None => println!("\nerror: {}", err)
+ }
+ }
+
+ fn fatal(&self, err: &str) -> ! {
+ self.error(err); panic!();
+ }
+
+ fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
+ self.try_print_open_handles();
+ self.error(err);
+ proc_res.fatal(None);
+ }
+
+ // This function is a poor man's attempt to debug rust-lang/rust#38620, if
+ // that's closed then this should be deleted
+ //
+ // This is a very "opportunistic" debugging attempt, so we ignore all
+ // errors here.
+ fn try_print_open_handles(&self) {
+ if !cfg!(windows) {
+ return
+ }
+ if self.config.mode != Incremental {
+ return
+ }
+
+ let filename = match self.testpaths.file.file_stem() {
+ Some(path) => path,
+ None => return,
+ };
+
+ let mut cmd = Command::new("handle.exe");
+ cmd.arg("-a").arg("-u");
+ cmd.arg(filename);
+ cmd.arg("-nobanner");
+ cmd.stdout(Stdio::piped());
+ cmd.stderr(Stdio::piped());
+ let output = match cmd.spawn().and_then(read2_abbreviated) {
+ Ok(output) => output,
+ Err(_) => return,
+ };
+ println!("---------------------------------------------------");
+ println!("ran extra command to debug rust-lang/rust#38620: ");
+ println!("{:?}", cmd);
+ println!("result: {}", output.status);
+ println!("--- stdout ----------------------------------------");
+ println!("{}", String::from_utf8_lossy(&output.stdout));
+ println!("--- stderr ----------------------------------------");
+ println!("{}", String::from_utf8_lossy(&output.stderr));
+ println!("---------------------------------------------------");
+ }
+
+ // codegen tests (using FileCheck)
+
+ fn compile_test_and_save_ir(&self) -> ProcRes {
+ let aux_dir = self.aux_output_dir_name();
+
+ let output_file = TargetLocation::ThisDirectory(
+ self.output_base_name().parent().unwrap().to_path_buf());
+ let mut rustc = self.make_compile_args(&self.testpaths.file, output_file, AllowUnused::No);
+ rustc.arg("-L").arg(aux_dir)
+ .arg("--emit=llvm-ir");
+
+ self.compose_and_run_compiler(rustc, None)
+ }
+
+ fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
+ // This works with both `--emit asm` (as default output name for the assembly)
+ // and `ptx-linker` because the latter can write output at requested location.
+ let output_path = self.output_base_name().with_extension("s");
+
+ let output_file = TargetLocation::ThisFile(output_path.clone());
+ let mut rustc = self.make_compile_args(&self.testpaths.file, output_file, AllowUnused::No);
+
+ rustc.arg("-L").arg(self.aux_output_dir_name());
+
+ match self.props.assembly_output.as_ref().map(AsRef::as_ref) {
+ Some("emit-asm") => {
+ rustc.arg("--emit=asm");
+ }
+
+ Some("ptx-linker") => {
+ // No extra flags needed.
+ }
+
+ Some(_) => self.fatal("unknown 'assembly-output' header"),
+ None => self.fatal("missing 'assembly-output' header"),
+ }
+
+ (self.compose_and_run_compiler(rustc, None), output_path)
+ }
+
+ fn verify_with_filecheck(&self, output: &Path) -> ProcRes {
+ let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
+ filecheck.arg("--input-file").arg(output)
+ .arg(&self.testpaths.file);
+ self.compose_and_run(filecheck, "", None, None)
+ }
+
+ fn run_codegen_test(&self) {
+ assert!(self.revision.is_none(), "revisions not relevant here");
+
+ if self.config.llvm_filecheck.is_none() {
+ self.fatal("missing --llvm-filecheck");
+ }
+
+ let proc_res = self.compile_test_and_save_ir();
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("compilation failed!", &proc_res);
+ }
+
+ let output_path = self.output_base_name().with_extension("ll");
+ let proc_res = self.verify_with_filecheck(&output_path);
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
+ }
+ }
+
+ fn run_assembly_test(&self) {
+ if self.config.llvm_filecheck.is_none() {
+ self.fatal("missing --llvm-filecheck");
+ }
+
+ let (proc_res, output_path) = self.compile_test_and_save_assembly();
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("compilation failed!", &proc_res);
+ }
+
+ let proc_res = self.verify_with_filecheck(&output_path);
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
+ }
+ }
+
+ fn charset() -> &'static str {
+ // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
+ if cfg!(target_os = "bitrig") {
+ "auto"
+ } else if cfg!(target_os = "freebsd") {
+ "ISO-8859-1"
+ } else {
+ "UTF-8"
+ }
+ }
+
+ fn run_rustdoc_test(&self) {
+ assert!(self.revision.is_none(), "revisions not relevant here");
+
+ let out_dir = self.output_base_name();
+ let _ = fs::remove_dir_all(&out_dir);
+ create_dir_all(&out_dir).unwrap();
+
+ let proc_res = self.document(&out_dir);
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("rustdoc failed!", &proc_res);
+ }
+
+ if self.props.check_test_line_numbers_match {
+ self.check_rustdoc_test_option(proc_res);
+ } else {
+ let root = self.config.find_rust_src_root().unwrap();
+ let res = self.cmd2procres(
+ Command::new(&self.config.docck_python)
+ .arg(root.join("src/etc/htmldocck.py"))
+ .arg(out_dir)
+ .arg(&self.testpaths.file),
+ );
+ if !res.status.success() {
+ self.fatal_proc_rec("htmldocck failed!", &res);
+ }
+ }
+ }
+
+ fn get_lines<P: AsRef<Path>>(&self, path: &P,
+ mut other_files: Option<&mut Vec<String>>) -> Vec<usize> {
+ let mut file = fs::File::open(path)
+ .expect("markdown_test_output_check_entry File::open failed");
+ let mut content = String::new();
+ file.read_to_string(&mut content)
+ .expect("markdown_test_output_check_entry read_to_string failed");
+ let mut ignore = false;
+ content.lines()
+ .enumerate()
+ .filter_map(|(line_nb, line)| {
+ if (line.trim_start().starts_with("pub mod ") ||
+ line.trim_start().starts_with("mod ")) &&
+ line.ends_with(';') {
+ if let Some(ref mut other_files) = other_files {
+ other_files.push(line.rsplit("mod ")
+ .next()
+ .unwrap()
+ .replace(";", ""));
+ }
+ None
+ } else {
+ let sline = line.split("///").last().unwrap_or("");
+ let line = sline.trim_start();
+ if line.starts_with("```") {
+ if ignore {
+ ignore = false;
+ None
+ } else {
+ ignore = true;
+ Some(line_nb + 1)
+ }
+ } else {
+ None
+ }
+ }
+ })
+ .collect()
+ }
+
+ fn check_rustdoc_test_option(&self, res: ProcRes) {
+ let mut other_files = Vec::new();
+ let mut files: HashMap<String, Vec<usize>> = HashMap::new();
+ let cwd = env::current_dir().unwrap();
+ files.insert(self.testpaths.file.strip_prefix(&cwd)
+ .unwrap_or(&self.testpaths.file)
+ .to_str()
+ .unwrap()
+ .replace('\\', "/"),
+ self.get_lines(&self.testpaths.file, Some(&mut other_files)));
+ for other_file in other_files {
+ let mut path = self.testpaths.file.clone();
+ path.set_file_name(&format!("{}.rs", other_file));
+ files.insert(path.strip_prefix(&cwd)
+ .unwrap_or(&path)
+ .to_str()
+ .unwrap()
+ .replace('\\', "/"),
+ self.get_lines(&path, None));
+ }
+
+ let mut tested = 0;
+ for _ in res.stdout.split('\n')
+ .filter(|s| s.starts_with("test "))
+ .inspect(|s| {
+ let tmp: Vec<&str> = s.split(" - ").collect();
+ if tmp.len() == 2 {
+ let path = tmp[0].rsplit("test ").next().unwrap();
+ if let Some(ref mut v) = files.get_mut(
+ &path.replace('\\', "/")) {
+ tested += 1;
+ let mut iter = tmp[1].split("(line ");
+ iter.next();
+ let line = iter.next()
+ .unwrap_or(")")
+ .split(')')
+ .next()
+ .unwrap_or("0")
+ .parse()
+ .unwrap_or(0);
+ if let Ok(pos) = v.binary_search(&line) {
+ v.remove(pos);
+ } else {
+ self.fatal_proc_rec(
+ &format!("Not found doc test: \"{}\" in \"{}\":{:?}",
+ s, path, v),
+ &res);
+ }
+ }
+ }
+ }) {}
+ if tested == 0 {
+ self.fatal_proc_rec(&format!("No test has been found... {:?}", files), &res);
+ } else {
+ for (entry, v) in &files {
+ if !v.is_empty() {
+ self.fatal_proc_rec(&format!("Not found test at line{} \"{}\":{:?}",
+ if v.len() > 1 { "s" } else { "" }, entry, v),
+ &res);
+ }
+ }
+ }
+ }
+
+ fn run_codegen_units_test(&self) {
+ assert!(self.revision.is_none(), "revisions not relevant here");
+
+ let proc_res = self.compile_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("compilation failed!", &proc_res);
+ }
+
+ self.check_no_compiler_crash(&proc_res);
+
+ const PREFIX: &'static str = "TRANS_ITEM ";
+ const CGU_MARKER: &'static str = "@@";
+
+ let actual: Vec<TransItem> = proc_res
+ .stdout
+ .lines()
+ .filter(|line| line.starts_with(PREFIX))
+ .map(str_to_trans_item)
+ .collect();
+
+ let expected: Vec<TransItem> = errors::load_errors(&self.testpaths.file, None)
+ .iter()
+ .map(|e| str_to_trans_item(&e.msg[..]))
+ .collect();
+
+ let mut missing = Vec::new();
+ let mut wrong_cgus = Vec::new();
+
+ for expected_item in &expected {
+ let actual_item_with_same_name = actual.iter()
+ .find(|ti| ti.name == expected_item.name);
+
+ if let Some(actual_item) = actual_item_with_same_name {
+ if !expected_item.codegen_units.is_empty() &&
+ // Also check for codegen units
+ expected_item.codegen_units != actual_item.codegen_units {
+ wrong_cgus.push((expected_item.clone(), actual_item.clone()));
+ }
+ } else {
+ missing.push(expected_item.string.clone());
+ }
+ }
+
+ let unexpected: Vec<_> =
+ actual.iter()
+ .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
+ .map(|acgu| acgu.string.clone())
+ .collect();
+
+ if !missing.is_empty() {
+ missing.sort();
+
+ println!("\nThese items should have been contained but were not:\n");
+
+ for item in &missing {
+ println!("{}", item);
+ }
+
+ println!("\n");
+ }
+
+ if !unexpected.is_empty() {
+ let sorted = {
+ let mut sorted = unexpected.clone();
+ sorted.sort();
+ sorted
+ };
+
+ println!("\nThese items were contained but should not have been:\n");
+
+ for item in sorted {
+ println!("{}", item);
+ }
+
+ println!("\n");
+ }
+
+ if !wrong_cgus.is_empty() {
+ wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
+ println!("\nThe following items were assigned to wrong codegen units:\n");
+
+ for &(ref expected_item, ref actual_item) in &wrong_cgus {
+ println!("{}", expected_item.name);
+ println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units));
+ println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units));
+ println!("");
+ }
+ }
+
+ if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty())
+ {
+ panic!();
+ }
+
+ #[derive(Clone, Eq, PartialEq)]
+ struct TransItem {
+ name: String,
+ codegen_units: HashSet<String>,
+ string: String,
+ }
+
+ // [TRANS_ITEM] name [@@ (cgu)+]
+ fn str_to_trans_item(s: &str) -> TransItem {
+ let s = if s.starts_with(PREFIX) {
+ (&s[PREFIX.len()..]).trim()
+ } else {
+ s.trim()
+ };
+
+ let full_string = format!("{}{}", PREFIX, s.trim().to_owned());
+
+ let parts: Vec<&str> = s.split(CGU_MARKER)
+ .map(str::trim)
+ .filter(|s| !s.is_empty())
+ .collect();
+
+ let name = parts[0].trim();
+
+ let cgus = if parts.len() > 1 {
+ let cgus_str = parts[1];
+
+ cgus_str.split(' ')
+ .map(str::trim)
+ .filter(|s| !s.is_empty())
+ .map(str::to_owned)
+ .collect()
+ }
+ else {
+ HashSet::new()
+ };
+
+ TransItem {
+ name: name.to_owned(),
+ codegen_units: cgus,
+ string: full_string,
+ }
+ }
+
+ fn codegen_units_to_str(cgus: &HashSet<String>) -> String
+ {
+ let mut cgus: Vec<_> = cgus.iter().collect();
+ cgus.sort();
+
+ let mut string = String::new();
+ for cgu in cgus {
+ string.push_str(&cgu[..]);
+ string.push_str(" ");
+ }
+
+ string
+ }
+ }
+
+ fn init_incremental_test(&self) {
+ // (See `run_incremental_test` for an overview of how incremental tests work.)
+
+ // Before any of the revisions have executed, create the
+ // incremental workproduct directory. Delete any old
+ // incremental work products that may be there from prior
+ // runs.
+ let incremental_dir = self.incremental_dir();
+ if incremental_dir.exists() {
+ // 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();
+
+ if self.config.verbose {
+ print!("init_incremental_test: incremental_dir={}", incremental_dir.display());
+ }
+ }
+
+ fn run_incremental_test(&self) {
+ // Basic plan for a test incremental/foo/bar.rs:
+ // - load list of revisions rpass1, cfail2, rpass3
+ // - each should begin with `rpass`, `cfail`, or `cfail`
+ // - if `rpass`, expect compile and execution to succeed
+ // - if `cfail`, expect compilation to fail
+ // - if `rfail`, expect execution to fail
+ // - create a directory build/foo/bar.incremental
+ // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass1
+ // - because name of revision starts with "rpass", expect success
+ // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C cfail2
+ // - because name of revision starts with "cfail", expect an error
+ // - load expected errors as usual, but filter for those that end in `[rfail2]`
+ // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass3
+ // - because name of revision starts with "rpass", expect success
+ // - execute build/foo/bar.exe and save output
+ //
+ // FIXME -- use non-incremental mode as an oracle? That doesn't apply
+ // to #[rustc_dirty] and clean tests I guess
+
+ let revision = self.revision.expect("incremental tests require a list of revisions");
+
+ // Incremental workproduct directory should have already been created.
+ let incremental_dir = self.incremental_dir();
+ assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
+
+ // Add an extra flag pointing at the incremental directory.
+ let mut revision_props = self.props.clone();
+ revision_props.incremental_dir = Some(incremental_dir);
+
+ let revision_cx = TestCx {
+ config: self.config,
+ props: &revision_props,
+ testpaths: self.testpaths,
+ revision: self.revision,
+ };
+
+ if self.config.verbose {
+ print!("revision={:?} revision_props={:#?}", revision, revision_props);
+ }
+
+ if revision.starts_with("rpass") {
+ revision_cx.run_rpass_test();
+ } else if revision.starts_with("rfail") {
+ revision_cx.run_rfail_test();
+ } else if revision.starts_with("cfail") {
+ revision_cx.run_cfail_test();
+ } else {
+ revision_cx.fatal(
+ "revision name must begin with rpass, rfail, or cfail");
+ }
+ }
+
+ /// Directory where incremental work products are stored.
+ fn incremental_dir(&self) -> PathBuf {
+ self.output_base_name().with_extension("inc")
+ }
+
+ fn run_rmake_test(&self) {
+ // FIXME(#11094): we should fix these tests
+ if self.config.host != self.config.target {
+ return
+ }
+
+ let cwd = env::current_dir().unwrap();
+ let src_root = self.config.src_base.parent().unwrap()
+ .parent().unwrap()
+ .parent().unwrap();
+ let src_root = cwd.join(&src_root);
+
+ let tmpdir = cwd.join(self.output_base_name());
+ if tmpdir.exists() {
+ self.aggressive_rm_rf(&tmpdir).unwrap();
+ }
+ create_dir_all(&tmpdir).unwrap();
+
+ let host = &self.config.host;
+ let make = if host.contains("bitrig") || host.contains("dragonfly") ||
+ host.contains("freebsd") || host.contains("netbsd") ||
+ host.contains("openbsd") {
+ "gmake"
+ } else {
+ "make"
+ };
+
+ let mut cmd = Command::new(make);
+ cmd.current_dir(&self.testpaths.file)
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .env("TARGET", &self.config.target)
+ .env("PYTHON", &self.config.docck_python)
+ .env("S", src_root)
+ .env("RUST_BUILD_STAGE", &self.config.stage_id)
+ .env("RUSTC", cwd.join(&self.config.rustc_path))
+ .env("RUSTDOC",
+ cwd.join(&self.config.rustdoc_path.as_ref().expect("--rustdoc-path passed")))
+ .env("TMPDIR", &tmpdir)
+ .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
+ .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
+ .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
+ .env("LLVM_COMPONENTS", &self.config.llvm_components)
+ .env("LLVM_CXXFLAGS", &self.config.llvm_cxxflags);
+
+ if let Some(ref linker) = self.config.linker {
+ cmd.env("RUSTC_LINKER", linker);
+ }
+
+ // We don't want RUSTFLAGS set from the outside to interfere with
+ // compiler flags set in the test cases:
+ cmd.env_remove("RUSTFLAGS");
+
+ if self.config.bless {
+ cmd.env("RUSTC_BLESS_TEST", "--bless");
+ // Assume this option is active if the environment variable is "defined", with _any_ value.
+ // As an example, a `Makefile` can use this option by:
+ //
+ // ifdef RUSTC_BLESS_TEST
+ // cp "$(TMPDIR)"/actual_something.ext expected_something.ext
+ // else
+ // $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext
+ // endif
+ }
+
+ if self.config.target.contains("msvc") {
+ // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
+ // and that `lib.exe` lives next to it.
+ let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
+
+ // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
+ // a path and instead passes `C:\msys64\foo`, so convert all
+ // `/`-arguments to MSVC here to `-` arguments.
+ let cflags = self.config.cflags.split(' ').map(|s| s.replace("/", "-"))
+ .collect::<Vec<_>>().join(" ");
+
+ cmd.env("IS_MSVC", "1")
+ .env("IS_WINDOWS", "1")
+ .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
+ .env("CC", format!("'{}' {}", self.config.cc, cflags))
+ .env("CXX", &self.config.cxx);
+ } else {
+ cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
+ .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags))
+ .env("AR", &self.config.ar);
+
+ if self.config.target.contains("windows") {
+ cmd.env("IS_WINDOWS", "1");
+ }
+ }
+
+ let output = cmd.spawn().and_then(read2_abbreviated).expect("failed to spawn `make`");
+ if !output.status.success() {
+ let res = ProcRes {
+ status: output.status,
+ stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
+ stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
+ cmdline: format!("{:?}", cmd),
+ };
+ self.fatal_proc_rec("make failed", &res);
+ }
+ }
+
+ fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
+ for e in path.read_dir()? {
+ let entry = e?;
+ let path = entry.path();
+ if entry.file_type()?.is_dir() {
+ self.aggressive_rm_rf(&path)?;
+ } else {
+ // Remove readonly files as well on windows (by default we can't)
+ fs::remove_file(&path).or_else(|e| {
+ if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied {
+ let mut meta = entry.metadata()?.permissions();
+ meta.set_readonly(false);
+ fs::set_permissions(&path, meta)?;
+ fs::remove_file(&path)
+ } else {
+ Err(e)
+ }
+ })?;
+ }
+ }
+ fs::remove_dir(path)
+ }
+
+ fn run_ui_test(&self) {
+ // if the user specified a format in the ui test
+ // print the output to the stderr file, otherwise extract
+ // the rendered error messages from json and print them
+ let explicit = self
+ .props
+ .compile_flags
+ .iter()
+ .any(|s| s.contains("--error-format"));
+ let proc_res = self.compile_test();
+
+ let expected_stderr_path = self.expected_output_path(UI_STDERR);
+ let expected_stderr = self.load_expected_output(&expected_stderr_path);
+
+ let expected_stdout_path = self.expected_output_path(UI_STDOUT);
+ let expected_stdout = self.load_expected_output(&expected_stdout_path);
+
+ let expected_fixed_path = self.expected_output_path(UI_FIXED);
+ let expected_fixed = self.load_expected_output(&expected_fixed_path);
+
+ let normalized_stdout =
+ self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout);
+
+ let stderr = if explicit {
+ proc_res.stderr.clone()
+ } else {
+ json::extract_rendered(&proc_res.stderr, &proc_res)
+ };
+
+ let normalized_stderr =
+ self.normalize_output(&stderr, &self.props.normalize_stderr);
+
+ let mut errors = 0;
+ errors += self.compare_output(UI_STDOUT, &normalized_stdout, &expected_stdout);
+ errors += self.compare_output(UI_STDERR, &normalized_stderr, &expected_stderr);
+
+
+ if self.config.rustfix_coverage {
+ // Find out which tests have `MachineApplicable` suggestions but are missing
+ // `run-rustfix` or `run-rustfix-only-machine-applicable` headers.
+ //
+ // This will return an empty `Vec` in case the executed test file has a
+ // `compile-flags: --error-format=xxxx` header with a value other than `json`.
+ let suggestions = get_suggestions_from_json(
+ &proc_res.stderr,
+ &HashSet::new(),
+ Filter::MachineApplicableOnly
+ ).unwrap_or_default();
+ if suggestions.len() > 0
+ && !self.props.run_rustfix
+ && !self.props.rustfix_only_machine_applicable {
+ let mut coverage_file_path = self.config.build_base.clone();
+ coverage_file_path.push("rustfix_missing_coverage.txt");
+ debug!("coverage_file_path: {}", coverage_file_path.display());
+
+ let mut file = OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(coverage_file_path.as_path())
+ .expect("could not create or open file");
+
+ if let Err(_) = writeln!(file, "{}", self.testpaths.file.display()) {
+ panic!("couldn't write to {}", coverage_file_path.display());
+ }
+ }
+ }
+
+ if self.props.run_rustfix {
+ // Apply suggestions from lints to the code itself
+ let unfixed_code = self
+ .load_expected_output_from_path(&self.testpaths.file)
+ .expect("Could not load output from path");
+ let suggestions = get_suggestions_from_json(
+ &proc_res.stderr,
+ &HashSet::new(),
+ if self.props.rustfix_only_machine_applicable {
+ Filter::MachineApplicableOnly
+ } else {
+ Filter::Everything
+ },
+ ).expect("Could not retrieve suggestions from JSON");
+ let fixed_code = apply_suggestions(&unfixed_code, &suggestions).expect(&format!(
+ "failed to apply suggestions for {:?} with rustfix",
+ self.testpaths.file
+ ));
+
+ errors += self.compare_output(UI_FIXED, &fixed_code, &expected_fixed);
+ } else if !expected_fixed.is_empty() {
+ panic!(
+ "the `// run-rustfix` directive wasn't found but a `*.fixed` \
+ file was found"
+ );
+ }
+
+ if errors > 0 {
+ println!("To update references, rerun the tests and pass the `--bless` flag");
+ let relative_path_to_file =
+ self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
+ println!(
+ "To only update this specific test, also pass `--test-args {}`",
+ relative_path_to_file.display(),
+ );
+ self.fatal_proc_rec(
+ &format!("{} errors occurred comparing output.", errors),
+ &proc_res,
+ );
+ }
+
+ if self.props.run_pass {
+ let proc_res = self.exec_compiled_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("test run failed!", &proc_res);
+ }
+ }
+
+ if self.props.run_rustfix {
+ // And finally, compile the fixed code and make sure it both
+ // succeeds and has no diagnostics.
+ let mut rustc = self.make_compile_args(
+ &self.testpaths.file.with_extension(UI_FIXED),
+ TargetLocation::ThisFile(self.make_exe_name()),
+ AllowUnused::No,
+ );
+ rustc.arg("-L").arg(&self.aux_output_dir_name());
+ let res = self.compose_and_run_compiler(rustc, None);
+ if !res.status.success() {
+ self.fatal_proc_rec("failed to compile fixed code", &res);
+ }
+ if !res.stderr.is_empty() && !self.props.rustfix_only_machine_applicable {
+ self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
+ }
+ }
+ }
+
+ 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![ExpectedLine::Elision];
+ 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();
+ curr_test_contents.push(ExpectedLine::Elision);
+ } else if l.is_empty() {
+ // ignore
+ } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." {
+ curr_test_contents.push(ExpectedLine::Elision)
+ } else if l.starts_with("// ") {
+ let (_, test_content) = l.split_at("// ".len());
+ curr_test_contents.push(ExpectedLine::Text(test_content));
+ }
+ }
+ }
+ }
+
+ fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) {
+ let t = |file| FileTime::from_last_modification_time(&fs::metadata(file).unwrap());
+ let source_file = &self.testpaths.file;
+ let output_time = t(output_file);
+ let source_time = t(source_file);
+ if source_time > output_time {
+ debug!("source file time: {:?} output file time: {:?}", source_time, output_time);
+ panic!("test source file `{}` is newer than potentially stale output file `{}`.",
+ source_file.display(), test_name);
+ }
+ }
+
+ fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&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);
+ if !output_file.exists() {
+ panic!("Output file `{}` from test does not exist",
+ output_file.into_os_string().to_string_lossy());
+ }
+ self.check_mir_test_timestamp(test_name, &output_file);
+
+ 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| {
+ if let &ExpectedLine::Text(l) = l {
+ !l.is_empty()
+ } else {
+ true
+ }
+ }).peekable();
+
+ let compare = |expected_line, dumped_line| {
+ let e_norm = normalize_mir_line(expected_line);
+ let d_norm = normalize_mir_line(dumped_line);
+ debug!("found: {:?}", d_norm);
+ debug!("expected: {:?}", e_norm);
+ e_norm == d_norm
+ };
+
+ let error = |expected_line, extra_msg| {
+ let normalize_all = dumped_string.lines()
+ .map(nocomment_mir_line)
+ .filter(|l| !l.is_empty())
+ .collect::<Vec<_>>()
+ .join("\n");
+ let f = |l: &ExpectedLine<_>| match l {
+ &ExpectedLine::Elision => "... (elided)".into(),
+ &ExpectedLine::Text(t) => t
+ };
+ let expected_content = expected_content.iter()
+ .map(|l| f(l))
+ .collect::<Vec<_>>()
+ .join("\n");
+ panic!("Did not find expected line, error: {}\n\
+ Actual Line: {:?}\n\
+ Expected:\n{}\n\
+ Actual:\n{}",
+ extra_msg,
+ expected_line,
+ expected_content,
+ normalize_all);
+ };
+
+ // We expect each non-empty line to appear consecutively, non-consecutive lines
+ // must be separated by at least one Elision
+ let mut start_block_line = None;
+ while let Some(dumped_line) = dumped_lines.next() {
+ match expected_lines.next() {
+ Some(&ExpectedLine::Text(expected_line)) => {
+ let normalized_expected_line = normalize_mir_line(expected_line);
+ if normalized_expected_line.contains(":{") {
+ start_block_line = Some(expected_line);
+ }
+
+ if !compare(expected_line, dumped_line) {
+ error!("{:?}", start_block_line);
+ error(expected_line,
+ format!("Mismatch in lines\nCurrnt block: {}\nExpected Line: {:?}",
+ start_block_line.unwrap_or("None"), dumped_line));
+ }
+ },
+ Some(&ExpectedLine::Elision) => {
+ // skip any number of elisions in a row.
+ while let Some(&&ExpectedLine::Elision) = expected_lines.peek() {
+ expected_lines.next();
+ }
+ if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() {
+ let mut found = compare(expected_line, dumped_line);
+ if found {
+ continue;
+ }
+ while let Some(dumped_line) = dumped_lines.next() {
+ found = compare(expected_line, dumped_line);
+ if found {
+ break;
+ }
+ }
+ if !found {
+ error(expected_line, "ran out of mir dump to match against".into());
+ }
+ }
+ },
+ None => {},
+ }
+ }
+ }
+
+ fn get_mir_dump_dir(&self) -> PathBuf {
+ let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
+ debug!("input_file: {:?}", self.testpaths.file);
+ mir_dump_dir.push(&self.testpaths.relative_dir);
+ mir_dump_dir.push(self.testpaths.file.file_stem().unwrap());
+ mir_dump_dir
+ }
+
+ fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
+ let cflags = self.props.compile_flags.join(" ");
+ let json = cflags.contains("--error-format json")
+ || cflags.contains("--error-format pretty-json")
+ || cflags.contains("--error-format=json")
+ || cflags.contains("--error-format=pretty-json")
+ || cflags.contains("--output-format json")
+ || cflags.contains("--output-format=json");
+
+ let mut normalized = output.to_string();
+
+ let mut normalize_path = |from: &Path, to: &str| {
+ let mut from = from.display().to_string();
+ if json {
+ from = from.replace("\\", "\\\\");
+ }
+ normalized = normalized.replace(&from, to);
+ };
+
+ let parent_dir = self.testpaths.file.parent().unwrap();
+ normalize_path(parent_dir, "$DIR");
+
+ if let Ok(src_dir_str) = std::env::var("CARGO_MANIFEST_DIR") {
+ let src_dir = Path::new(&src_dir_str);
+ normalize_path(src_dir, "$SRC_DIR");
+ }
+
+ // Paths into the build directory
+ let test_build_dir = &self.config.build_base;
+ normalize_path(test_build_dir, "$TEST_BUILD_DIR");
+
+ if json {
+ // escaped newlines in json strings should be readable
+ // in the stderr files. There's no point int being correct,
+ // since only humans process the stderr files.
+ // Thus we just turn escaped newlines back into newlines.
+ normalized = normalized.replace("\\n", "\n");
+ }
+
+ normalized = normalized.replace("\\\\", "\\") // denormalize for paths on windows
+ .replace("\\", "/") // normalize for paths on windows
+ .replace("\r\n", "\n") // normalize for linebreaks on windows
+ .replace("\t", "\\t"); // makes tabs visible
+ for rule in custom_rules {
+ let re = Regex::new(&rule.0).expect("bad regex in custom normalization rule");
+ normalized = re.replace_all(&normalized, &rule.1[..]).into_owned();
+ }
+ normalized
+ }
+
+ fn expected_output_path(&self, kind: &str) -> PathBuf {
+ let mut path =
+ expected_output_path(&self.testpaths, self.revision, kind);
+
+ if !path.exists() {
+ path = expected_output_path(&self.testpaths, self.revision, kind);
+ }
+
+ path
+ }
+
+ fn load_expected_output(&self, path: &Path) -> String {
+ if !path.exists() {
+ return String::new();
+ }
+
+ let mut result = String::new();
+ match File::open(path).and_then(|mut f| f.read_to_string(&mut result)) {
+ Ok(_) => result,
+ Err(e) => {
+ self.fatal(&format!("failed to load expected output from `{}`: {}",
+ path.display(), e))
+ }
+ }
+ }
+
+ fn load_expected_output_from_path(&self, path: &Path) -> Result<String, String> {
+ fs::read_to_string(path).map_err(|err| {
+ format!("failed to load expected output from `{}`: {}", path.display(), err)
+ })
+ }
+
+ fn delete_file(&self, file: &PathBuf) {
+ if !file.exists() {
+ // Deleting a nonexistant file would error.
+ return;
+ }
+ if let Err(e) = fs::remove_file(file) {
+ self.fatal(&format!("failed to delete `{}`: {}", file.display(), e,));
+ }
+ }
+
+ fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
+ if actual == expected {
+ return 0;
+ }
+
+ if !self.config.bless {
+ if expected.is_empty() {
+ println!("normalized {}:\n{}\n", kind, actual);
+ } else {
+ println!("diff of {}:\n", kind);
+ for diff in diff::lines(expected, actual) {
+ match diff {
+ diff::Result::Left(l) => println!("-{}", l),
+ diff::Result::Both(l, _) => println!(" {}", l),
+ diff::Result::Right(r) => println!("+{}", r),
+ }
+ }
+ }
+ }
+
+ let output_file = self
+ .output_base_name()
+ .with_extra_extension(self.revision.unwrap_or(""))
+ .with_extra_extension(kind);
+
+ let mut files = vec![output_file];
+ if self.config.bless {
+ files.push(expected_output_path(
+ self.testpaths,
+ self.revision,
+ kind,
+ ));
+ }
+
+ for output_file in &files {
+ if actual.is_empty() {
+ self.delete_file(output_file);
+ } else if let Err(err) = fs::write(&output_file, &actual) {
+ self.fatal(&format!(
+ "failed to write {} to `{}`: {}",
+ kind,
+ output_file.display(),
+ err,
+ ));
+ }
+ }
+
+ println!("\nThe actual {0} differed from the expected {0}.", kind);
+ for output_file in files {
+ println!("Actual {} saved to {}", kind, output_file.display());
+ }
+ if self.config.bless { 0 } else { 1 }
+ }
+}
+
+struct ProcArgs {
+ prog: String,
+ args: Vec<String>,
+}
+
+pub struct ProcRes {
+ status: ExitStatus,
+ stdout: String,
+ stderr: String,
+ cmdline: String,
+}
+
+impl ProcRes {
+ pub fn fatal(&self, err: Option<&str>) -> ! {
+ if let Some(e) = err {
+ println!("\nerror: {}", e);
+ }
+ print!("\
+ status: {}\n\
+ command: {}\n\
+ stdout:\n\
+ ------------------------------------------\n\
+ {}\n\
+ ------------------------------------------\n\
+ stderr:\n\
+ ------------------------------------------\n\
+ {}\n\
+ ------------------------------------------\n\
+ \n",
+ self.status, self.cmdline, self.stdout,
+ self.stderr);
+ // Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
+ // compiletest, which is unnecessary noise.
+ std::panic::resume_unwind(Box::new(()));
+ }
+}
+
+enum TargetLocation {
+ ThisFile(PathBuf),
+ ThisDirectory(PathBuf),
+}
+
+#[derive(Clone, PartialEq, Eq)]
+enum ExpectedLine<T: AsRef<str>> {
+ Elision,
+ Text(T)
+}
+
+enum AllowUnused {
+ Yes,
+ No,
+}
+
+impl<T> fmt::Debug for ExpectedLine<T>
+where
+ T: AsRef<str> + fmt::Debug
+{
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ if let &ExpectedLine::Text(ref t) = self {
+ write!(formatter, "{:?}", t)
+ } else {
+ write!(formatter, "\"...\" (Elision)")
+ }
+ }
+}
+
+fn normalize_mir_line(line: &str) -> String {
+ nocomment_mir_line(line).replace(char::is_whitespace, "")
+}
+
+fn nocomment_mir_line(line: &str) -> &str {
+ if let Some(idx) = line.find("//") {
+ let (l, _) = line.split_at(idx);
+ l.trim_end()
+ } else {
+ line
+ }
+}
+
+fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
+ use std::mem::replace;
+ use read2::read2;
+
+ const HEAD_LEN: usize = 160 * 1024;
+ const TAIL_LEN: usize = 256 * 1024;
+
+ enum ProcOutput {
+ Full(Vec<u8>),
+ Abbreviated {
+ head: Vec<u8>,
+ skipped: usize,
+ tail: Box<[u8]>,
+ }
+ }
+
+ impl ProcOutput {
+ fn extend(&mut self, data: &[u8]) {
+ let new_self = match *self {
+ ProcOutput::Full(ref mut bytes) => {
+ bytes.extend_from_slice(data);
+ let new_len = bytes.len();
+ if new_len <= HEAD_LEN + TAIL_LEN {
+ return;
+ }
+ let tail = bytes.split_off(new_len - TAIL_LEN).into_boxed_slice();
+ let head = replace(bytes, Vec::new());
+ let skipped = new_len - HEAD_LEN - TAIL_LEN;
+ ProcOutput::Abbreviated { head, skipped, tail }
+ }
+ ProcOutput::Abbreviated { ref mut skipped, ref mut tail, .. } => {
+ *skipped += data.len();
+ if data.len() <= TAIL_LEN {
+ tail[..data.len()].copy_from_slice(data);
+ tail.rotate_left(data.len());
+ } else {
+ tail.copy_from_slice(&data[(data.len() - TAIL_LEN)..]);
+ }
+ return;
+ }
+ };
+ *self = new_self;
+ }
+
+ fn into_bytes(self) -> Vec<u8> {
+ match self {
+ ProcOutput::Full(bytes) => bytes,
+ ProcOutput::Abbreviated { mut head, skipped, tail } => {
+ write!(&mut head, "\n\n<<<<<< SKIPPED {} BYTES >>>>>>\n\n", skipped).unwrap();
+ head.extend_from_slice(&tail);
+ head
+ }
+ }
+ }
+ }
+
+ let mut stdout = ProcOutput::Full(Vec::new());
+ let mut stderr = ProcOutput::Full(Vec::new());
+
+ drop(child.stdin.take());
+ read2(child.stdout.take().unwrap(), child.stderr.take().unwrap(), &mut |is_stdout, data, _| {
+ if is_stdout { &mut stdout } else { &mut stderr }.extend(data);
+ data.clear();
+ })?;
+ let status = child.wait()?;
+
+ Ok(Output {
+ status,
+ stdout: stdout.into_bytes(),
+ stderr: stderr.into_bytes(),
+ })
+}
diff --git a/vendor/compiletest_rs/src/uidiff.rs b/vendor/compiletest_rs/src/uidiff.rs
new file mode 100644
index 000000000..fca01029c
--- /dev/null
+++ b/vendor/compiletest_rs/src/uidiff.rs
@@ -0,0 +1,73 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Code for checking whether the output of the compiler matches what is
+//! expected.
+
+pub fn diff_lines(actual: &str, expected: &str) -> Vec<String> {
+ // 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))
+ }
+ }
+ (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 {
+ for (i, part) in expected.split("[..]").enumerate() {
+ match actual.find(part) {
+ Some(j) => {
+ if i == 0 && j != 0 {
+ return false;
+ }
+ actual = &actual[j + part.len()..];
+ }
+ None => return false,
+ }
+ }
+ actual.is_empty() || expected.ends_with("[..]")
+}
+
+struct ZipAll<I1: Iterator, I2: Iterator> {
+ first: I1,
+ second: I2,
+}
+
+impl<T, I1: Iterator<Item = T>, I2: Iterator<Item = T>> Iterator for ZipAll<I1, I2> {
+ type Item = (Option<T>, Option<T>);
+ fn next(&mut self) -> Option<(Option<T>, Option<T>)> {
+ let first = self.first.next();
+ let second = self.second.next();
+
+ match (first, second) {
+ (None, None) => None,
+ (a, b) => Some((a, b)),
+ }
+ }
+}
+
+fn zip_all<T, I1: Iterator<Item = T>, I2: Iterator<Item = T>>(a: I1, b: I2) -> ZipAll<I1, I2> {
+ ZipAll {
+ first: a,
+ second: b,
+ }
+}
diff --git a/vendor/compiletest_rs/src/util.rs b/vendor/compiletest_rs/src/util.rs
new file mode 100644
index 000000000..1290fe24e
--- /dev/null
+++ b/vendor/compiletest_rs/src/util.rs
@@ -0,0 +1,134 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::env;
+use common::Config;
+use std::ffi::OsStr;
+use std::path::PathBuf;
+
+/// 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"),
+ ("haiku", "haiku"),
+ ("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"),
+ ("i586", "x86"),
+ ("i686", "x86"),
+ ("loongarch64", "loongarch64"),
+ ("mips", "mips"),
+ ("msp430", "msp430"),
+ ("nvptx64", "nvptx64"),
+ ("powerpc", "powerpc"),
+ ("powerpc64", "powerpc64"),
+ ("s390x", "s390x"),
+ ("sparc", "sparc"),
+ ("x86_64", "x86_64"),
+ ("xcore", "xcore"),
+ ("asmjs", "asmjs"),
+ ("wasm32", "wasm32"),
+];
+
+pub fn matches_os(triple: &str, name: &str) -> bool {
+ // For the wasm32 bare target we ignore anything also ignored on emscripten
+ // and then we also recognize `wasm32-bare` as the os for the target
+ if triple == "wasm32-unknown-unknown" {
+ return name == "emscripten" || name == "wasm32-bare"
+ }
+ for &(triple_os, os) in OS_TABLE {
+ if triple.contains(triple_os) {
+ return os == name;
+ }
+ }
+ false
+}
+pub fn get_arch(triple: &str) -> &str {
+ for &(triple_arch, arch) in ARCH_TABLE {
+ if triple.contains(triple_arch) {
+ return arch;
+ }
+ }
+ triple.split('-').nth(0).unwrap()
+}
+
+pub fn get_env(triple: &str) -> Option<&str> {
+ triple.split('-').nth(3)
+}
+
+pub fn get_pointer_width(triple: &str) -> &'static str {
+ if (triple.contains("64") && !triple.ends_with("gnux32")) || triple.starts_with("s390x") {
+ "64bit"
+ } else {
+ "32bit"
+ }
+}
+
+pub fn make_new_path(path: &str) -> String {
+ assert!(cfg!(windows));
+ // 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(),
+ }
+}
+
+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);
+ }
+}
+
+pub trait PathBufExt {
+ /// Append an extension to the path, even if it already has one.
+ fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf;
+}
+
+impl PathBufExt for PathBuf {
+ fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
+ if extension.as_ref().is_empty() {
+ self.clone()
+ } else {
+ let mut fname = self.file_name().unwrap().to_os_string();
+ if !extension.as_ref().to_str().unwrap().starts_with('.') {
+ fname.push(".");
+ }
+ fname.push(extension);
+ self.with_file_name(fname)
+ }
+ }
+}