]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/runtest.rs
test for #50518
[rust.git] / src / tools / compiletest / src / runtest.rs
1 // ignore-tidy-filelength
2
3 use crate::common::CompareMode;
4 use crate::common::{expected_output_path, UI_EXTENSIONS, UI_FIXED, UI_STDERR, UI_STDOUT};
5 use crate::common::{output_base_dir, output_base_name, output_testname_unique};
6 use crate::common::{Codegen, CodegenUnits, DebugInfoBoth, DebugInfoGdb, DebugInfoLldb, Rustdoc};
7 use crate::common::{CompileFail, Pretty, RunFail, RunPass, RunPassValgrind};
8 use crate::common::{Config, TestPaths};
9 use crate::common::{Incremental, MirOpt, RunMake, Ui, JsDocTest, Assembly};
10 use diff;
11 use crate::errors::{self, Error, ErrorKind};
12 use filetime::FileTime;
13 use crate::header::TestProps;
14 use crate::json;
15 use regex::{Captures, Regex};
16 use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
17 use crate::util::{logv, PathBufExt};
18
19 use std::collections::hash_map::DefaultHasher;
20 use std::collections::{HashMap, HashSet, VecDeque};
21 use std::env;
22 use std::ffi::{OsStr, OsString};
23 use std::fmt;
24 use std::fs::{self, create_dir_all, File, OpenOptions};
25 use std::hash::{Hash, Hasher};
26 use std::io::prelude::*;
27 use std::io::{self, BufReader};
28 use std::path::{Path, PathBuf};
29 use std::process::{Child, Command, ExitStatus, Output, Stdio};
30 use std::str;
31
32 use crate::extract_gdb_version;
33 use crate::is_android_gdb_target;
34
35 #[cfg(windows)]
36 fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
37     use std::sync::Mutex;
38     const SEM_NOGPFAULTERRORBOX: u32 = 0x0002;
39     extern "system" {
40         fn SetErrorMode(mode: u32) -> u32;
41     }
42
43     lazy_static! {
44         static ref LOCK: Mutex<()> = { Mutex::new(()) };
45     }
46     // Error mode is a global variable, so lock it so only one thread will change it
47     let _lock = LOCK.lock().unwrap();
48
49     // Tell Windows to not show any UI on errors (such as terminating abnormally).
50     // This is important for running tests, since some of them use abnormal
51     // termination by design. This mode is inherited by all child processes.
52     unsafe {
53         let old_mode = SetErrorMode(SEM_NOGPFAULTERRORBOX); // read inherited flags
54         SetErrorMode(old_mode | SEM_NOGPFAULTERRORBOX);
55         let r = f();
56         SetErrorMode(old_mode);
57         r
58     }
59 }
60
61 #[cfg(not(windows))]
62 fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
63     f()
64 }
65
66 /// The name of the environment variable that holds dynamic library locations.
67 pub fn dylib_env_var() -> &'static str {
68     if cfg!(windows) {
69         "PATH"
70     } else if cfg!(target_os = "macos") {
71         "DYLD_LIBRARY_PATH"
72     } else if cfg!(target_os = "haiku") {
73         "LIBRARY_PATH"
74     } else {
75         "LD_LIBRARY_PATH"
76     }
77 }
78
79 /// The platform-specific library name
80 pub fn get_lib_name(lib: &str, dylib: bool) -> String {
81     // In some casess (e.g. MUSL), we build a static
82     // library, rather than a dynamic library.
83     // In this case, the only path we can pass
84     // with '--extern-meta' is the '.lib' file
85     if !dylib {
86         return format!("lib{}.rlib", lib);
87     }
88
89     if cfg!(windows) {
90         format!("{}.dll", lib)
91     } else if cfg!(target_os = "macos") {
92         format!("lib{}.dylib", lib)
93     } else {
94         format!("lib{}.so", lib)
95     }
96 }
97
98 #[derive(Debug, PartialEq)]
99 pub enum DiffLine {
100     Context(String),
101     Expected(String),
102     Resulting(String),
103 }
104
105 #[derive(Debug, PartialEq)]
106 pub struct Mismatch {
107     pub line_number: u32,
108     pub lines: Vec<DiffLine>,
109 }
110
111 impl Mismatch {
112     fn new(line_number: u32) -> Mismatch {
113         Mismatch {
114             line_number: line_number,
115             lines: Vec::new(),
116         }
117     }
118 }
119
120 // Produces a diff between the expected output and actual output.
121 pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
122     let mut line_number = 1;
123     let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
124     let mut lines_since_mismatch = context_size + 1;
125     let mut results = Vec::new();
126     let mut mismatch = Mismatch::new(0);
127
128     for result in diff::lines(expected, actual) {
129         match result {
130             diff::Result::Left(str) => {
131                 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
132                     results.push(mismatch);
133                     mismatch = Mismatch::new(line_number - context_queue.len() as u32);
134                 }
135
136                 while let Some(line) = context_queue.pop_front() {
137                     mismatch.lines.push(DiffLine::Context(line.to_owned()));
138                 }
139
140                 mismatch.lines.push(DiffLine::Expected(str.to_owned()));
141                 line_number += 1;
142                 lines_since_mismatch = 0;
143             }
144             diff::Result::Right(str) => {
145                 if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
146                     results.push(mismatch);
147                     mismatch = Mismatch::new(line_number - context_queue.len() as u32);
148                 }
149
150                 while let Some(line) = context_queue.pop_front() {
151                     mismatch.lines.push(DiffLine::Context(line.to_owned()));
152                 }
153
154                 mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
155                 lines_since_mismatch = 0;
156             }
157             diff::Result::Both(str, _) => {
158                 if context_queue.len() >= context_size {
159                     let _ = context_queue.pop_front();
160                 }
161
162                 if lines_since_mismatch < context_size {
163                     mismatch.lines.push(DiffLine::Context(str.to_owned()));
164                 } else if context_size > 0 {
165                     context_queue.push_back(str);
166                 }
167
168                 line_number += 1;
169                 lines_since_mismatch += 1;
170             }
171         }
172     }
173
174     results.push(mismatch);
175     results.remove(0);
176
177     results
178 }
179
180 pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
181     match &*config.target {
182         "arm-linux-androideabi"
183         | "armv7-linux-androideabi"
184         | "thumbv7neon-linux-androideabi"
185         | "aarch64-linux-android" => {
186             if !config.adb_device_status {
187                 panic!("android device not available");
188             }
189         }
190
191         _ => {
192             // android has its own gdb handling
193             if config.mode == DebugInfoGdb && config.gdb.is_none() {
194                 panic!("gdb not available but debuginfo gdb debuginfo test requested");
195             }
196         }
197     }
198
199     if config.verbose {
200         // We're going to be dumping a lot of info. Start on a new line.
201         print!("\n\n");
202     }
203     debug!("running {:?}", testpaths.file.display());
204     let props = TestProps::from_file(&testpaths.file, revision, &config);
205
206     let cx = TestCx {
207         config: &config,
208         props: &props,
209         testpaths,
210         revision: revision,
211     };
212     create_dir_all(&cx.output_base_dir()).unwrap();
213
214     if config.mode == Incremental {
215         // Incremental tests are special because they cannot be run in
216         // parallel.
217         assert!(
218             !props.revisions.is_empty(),
219             "Incremental tests require revisions."
220         );
221         cx.init_incremental_test();
222         for revision in &props.revisions {
223             let revision_props = TestProps::from_file(&testpaths.file, Some(revision), &config);
224             let rev_cx = TestCx {
225                 config: &config,
226                 props: &revision_props,
227                 testpaths,
228                 revision: Some(revision),
229             };
230             rev_cx.run_revision();
231         }
232     } else {
233         cx.run_revision();
234     }
235
236     cx.create_stamp();
237 }
238
239 pub fn compute_stamp_hash(config: &Config) -> String {
240     let mut hash = DefaultHasher::new();
241     config.stage_id.hash(&mut hash);
242
243     if config.mode == DebugInfoGdb || config.mode == DebugInfoBoth {
244         match config.gdb {
245             None => env::var_os("PATH").hash(&mut hash),
246             Some(ref s) if s.is_empty() => env::var_os("PATH").hash(&mut hash),
247             Some(ref s) => s.hash(&mut hash),
248         };
249     }
250
251     if config.mode == DebugInfoLldb || config.mode == DebugInfoBoth {
252         env::var_os("PATH").hash(&mut hash);
253         env::var_os("PYTHONPATH").hash(&mut hash);
254     }
255
256     format!("{:x}", hash.finish())
257 }
258
259 struct TestCx<'test> {
260     config: &'test Config,
261     props: &'test TestProps,
262     testpaths: &'test TestPaths,
263     revision: Option<&'test str>,
264 }
265
266 struct DebuggerCommands {
267     commands: Vec<String>,
268     check_lines: Vec<String>,
269     breakpoint_lines: Vec<usize>,
270 }
271
272 enum ReadFrom {
273     Path,
274     Stdin(String),
275 }
276
277 impl<'test> TestCx<'test> {
278     /// Code executed for each revision in turn (or, if there are no
279     /// revisions, exactly once, with revision == None).
280     fn run_revision(&self) {
281         match self.config.mode {
282             CompileFail => self.run_cfail_test(),
283             RunFail => self.run_rfail_test(),
284             RunPassValgrind => self.run_valgrind_test(),
285             Pretty => self.run_pretty_test(),
286             DebugInfoBoth => {
287                 self.run_debuginfo_gdb_test();
288                 self.run_debuginfo_lldb_test();
289             },
290             DebugInfoGdb => self.run_debuginfo_gdb_test(),
291             DebugInfoLldb => self.run_debuginfo_lldb_test(),
292             Codegen => self.run_codegen_test(),
293             Rustdoc => self.run_rustdoc_test(),
294             CodegenUnits => self.run_codegen_units_test(),
295             Incremental => self.run_incremental_test(),
296             RunMake => self.run_rmake_test(),
297             RunPass | Ui => self.run_ui_test(),
298             MirOpt => self.run_mir_opt_test(),
299             Assembly => self.run_assembly_test(),
300             JsDocTest => self.run_js_doc_test(),
301         }
302     }
303
304     fn should_run_successfully(&self) -> bool {
305         let run_pass = match self.config.mode {
306             RunPass => true,
307             Ui => self.props.run_pass,
308             _ => unimplemented!(),
309         };
310         return run_pass && !self.props.skip_codegen;
311     }
312
313     fn should_compile_successfully(&self) -> bool {
314         match self.config.mode {
315             CompileFail => self.props.compile_pass,
316             RunPass => true,
317             JsDocTest => true,
318             Ui => self.props.compile_pass,
319             Incremental => {
320                 let revision = self.revision
321                     .expect("incremental tests require a list of revisions");
322                 if revision.starts_with("rpass") || revision.starts_with("rfail") {
323                     true
324                 } else if revision.starts_with("cfail") {
325                     // FIXME: would be nice if incremental revs could start with "cpass"
326                     self.props.compile_pass
327                 } else {
328                     panic!("revision name must begin with rpass, rfail, or cfail");
329                 }
330             }
331             mode => panic!("unimplemented for mode {:?}", mode),
332         }
333     }
334
335     fn check_if_test_should_compile(&self, proc_res: &ProcRes) {
336         if self.should_compile_successfully() {
337             if !proc_res.status.success() {
338                 self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res);
339             }
340         } else {
341             if proc_res.status.success() {
342                 self.fatal_proc_rec(
343                     &format!("{} test compiled successfully!", self.config.mode)[..],
344                     proc_res,
345                 );
346             }
347
348             self.check_correct_failure_status(proc_res);
349         }
350     }
351
352     fn run_cfail_test(&self) {
353         let proc_res = self.compile_test();
354         self.check_if_test_should_compile(&proc_res);
355         self.check_no_compiler_crash(&proc_res);
356
357         let output_to_check = self.get_output(&proc_res);
358         let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
359         if !expected_errors.is_empty() {
360             if !self.props.error_patterns.is_empty() {
361                 self.fatal("both error pattern and expected errors specified");
362             }
363             self.check_expected_errors(expected_errors, &proc_res);
364         } else {
365             self.check_error_patterns(&output_to_check, &proc_res);
366         }
367
368         self.check_forbid_output(&output_to_check, &proc_res);
369     }
370
371     fn run_rfail_test(&self) {
372         let proc_res = self.compile_test();
373
374         if !proc_res.status.success() {
375             self.fatal_proc_rec("compilation failed!", &proc_res);
376         }
377
378         let proc_res = self.exec_compiled_test();
379
380         // The value our Makefile configures valgrind to return on failure
381         const VALGRIND_ERR: i32 = 100;
382         if proc_res.status.code() == Some(VALGRIND_ERR) {
383             self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
384         }
385
386         let output_to_check = self.get_output(&proc_res);
387         self.check_correct_failure_status(&proc_res);
388         self.check_error_patterns(&output_to_check, &proc_res);
389     }
390
391     fn get_output(&self, proc_res: &ProcRes) -> String {
392         if self.props.check_stdout {
393             format!("{}{}", proc_res.stdout, proc_res.stderr)
394         } else {
395             proc_res.stderr.clone()
396         }
397     }
398
399     fn check_correct_failure_status(&self, proc_res: &ProcRes) {
400         let expected_status = Some(self.props.failure_status);
401         let received_status = proc_res.status.code();
402
403         if expected_status != received_status {
404             self.fatal_proc_rec(
405                 &format!(
406                     "Error: expected failure status ({:?}) but received status {:?}.",
407                     expected_status, received_status
408                 ),
409                 proc_res,
410             );
411         }
412     }
413
414     fn run_rpass_test(&self) {
415         let proc_res = self.compile_test();
416
417         if !proc_res.status.success() {
418             self.fatal_proc_rec("compilation failed!", &proc_res);
419         }
420
421         // FIXME(#41968): Move this check to tidy?
422         let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
423         assert!(
424             expected_errors.is_empty(),
425             "run-pass tests with expected warnings should be moved to ui/"
426         );
427
428         if !self.props.skip_codegen {
429             let proc_res = self.exec_compiled_test();
430             if !proc_res.status.success() {
431                 self.fatal_proc_rec("test run failed!", &proc_res);
432             }
433         }
434     }
435
436     fn run_valgrind_test(&self) {
437         assert!(self.revision.is_none(), "revisions not relevant here");
438
439         if self.config.valgrind_path.is_none() {
440             assert!(!self.config.force_valgrind);
441             return self.run_rpass_test();
442         }
443
444         let mut proc_res = self.compile_test();
445
446         if !proc_res.status.success() {
447             self.fatal_proc_rec("compilation failed!", &proc_res);
448         }
449
450         let mut new_config = self.config.clone();
451         new_config.runtool = new_config.valgrind_path.clone();
452         let new_cx = TestCx {
453             config: &new_config,
454             ..*self
455         };
456         proc_res = new_cx.exec_compiled_test();
457
458         if !proc_res.status.success() {
459             self.fatal_proc_rec("test run failed!", &proc_res);
460         }
461     }
462
463     fn run_pretty_test(&self) {
464         if self.props.pp_exact.is_some() {
465             logv(self.config, "testing for exact pretty-printing".to_owned());
466         } else {
467             logv(
468                 self.config,
469                 "testing for converging pretty-printing".to_owned(),
470             );
471         }
472
473         let rounds = match self.props.pp_exact {
474             Some(_) => 1,
475             None => 2,
476         };
477
478         let src = fs::read_to_string(&self.testpaths.file).unwrap();
479         let mut srcs = vec![src];
480
481         let mut round = 0;
482         while round < rounds {
483             logv(
484                 self.config,
485                 format!(
486                     "pretty-printing round {} revision {:?}",
487                     round, self.revision
488                 ),
489             );
490             let read_from = if round == 0 {
491                 ReadFrom::Path
492             } else {
493                 ReadFrom::Stdin(srcs[round].to_owned())
494             };
495
496             let proc_res = self.print_source(read_from,
497                                              &self.props.pretty_mode);
498             if !proc_res.status.success() {
499                 self.fatal_proc_rec(
500                     &format!(
501                         "pretty-printing failed in round {} revision {:?}",
502                         round, self.revision
503                     ),
504                     &proc_res,
505                 );
506             }
507
508             let ProcRes { stdout, .. } = proc_res;
509             srcs.push(stdout);
510             round += 1;
511         }
512
513         let mut expected = match self.props.pp_exact {
514             Some(ref file) => {
515                 let filepath = self.testpaths.file.parent().unwrap().join(file);
516                 fs::read_to_string(&filepath).unwrap()
517             }
518             None => srcs[srcs.len() - 2].clone(),
519         };
520         let mut actual = srcs[srcs.len() - 1].clone();
521
522         if self.props.pp_exact.is_some() {
523             // Now we have to care about line endings
524             let cr = "\r".to_owned();
525             actual = actual.replace(&cr, "").to_owned();
526             expected = expected.replace(&cr, "").to_owned();
527         }
528
529         self.compare_source(&expected, &actual);
530
531         // If we're only making sure that the output matches then just stop here
532         if self.props.pretty_compare_only {
533             return;
534         }
535
536         // Finally, let's make sure it actually appears to remain valid code
537         let proc_res = self.typecheck_source(actual);
538         if !proc_res.status.success() {
539             self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
540         }
541
542         if !self.props.pretty_expanded {
543             return;
544         }
545
546         // additionally, run `--pretty expanded` and try to build it.
547         let proc_res = self.print_source(ReadFrom::Path, "expanded");
548         if !proc_res.status.success() {
549             self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
550         }
551
552         let ProcRes {
553             stdout: expanded_src,
554             ..
555         } = proc_res;
556         let proc_res = self.typecheck_source(expanded_src);
557         if !proc_res.status.success() {
558             self.fatal_proc_rec(
559                 "pretty-printed source (expanded) does not typecheck",
560                 &proc_res,
561             );
562         }
563     }
564
565     fn print_source(&self, read_from: ReadFrom, pretty_type: &str) -> ProcRes {
566         let aux_dir = self.aux_output_dir_name();
567         let input: &str = match read_from {
568             ReadFrom::Stdin(_) => "-",
569             ReadFrom::Path => self.testpaths.file.to_str().unwrap(),
570         };
571
572         let mut rustc = Command::new(&self.config.rustc_path);
573         rustc
574             .arg(input)
575             .args(&["-Z", &format!("unpretty={}", pretty_type)])
576             .args(&["--target", &self.config.target])
577             .arg("-L")
578             .arg(&aux_dir)
579             .args(&self.props.compile_flags)
580             .envs(self.props.exec_env.clone());
581         self.maybe_add_external_args(&mut rustc,
582                                      self.split_maybe_args(&self.config.target_rustcflags));
583
584         let src = match read_from {
585             ReadFrom::Stdin(src) => Some(src),
586             ReadFrom::Path => None
587         };
588
589         self.compose_and_run(
590             rustc,
591             self.config.compile_lib_path.to_str().unwrap(),
592             Some(aux_dir.to_str().unwrap()),
593             src,
594         )
595     }
596
597     fn compare_source(&self, expected: &str, actual: &str) {
598         if expected != actual {
599             self.fatal(&format!(
600                 "pretty-printed source does not match expected source\n\
601                  expected:\n\
602                  ------------------------------------------\n\
603                  {}\n\
604                  ------------------------------------------\n\
605                  actual:\n\
606                  ------------------------------------------\n\
607                  {}\n\
608                  ------------------------------------------\n\
609                  \n",
610                 expected, actual)
611             );
612         }
613     }
614
615     fn set_revision_flags(&self, cmd: &mut Command) {
616         if let Some(revision) = self.revision {
617             // Normalize revisions to be lowercase and replace `-`s with `_`s.
618             // Otherwise the `--cfg` flag is not valid.
619             let normalized_revision = revision.to_lowercase().replace("-", "_");
620             cmd.args(&["--cfg", &normalized_revision]);
621         }
622     }
623
624     fn typecheck_source(&self, src: String) -> ProcRes {
625         let mut rustc = Command::new(&self.config.rustc_path);
626
627         let out_dir = self.output_base_name().with_extension("pretty-out");
628         let _ = fs::remove_dir_all(&out_dir);
629         create_dir_all(&out_dir).unwrap();
630
631         let target = if self.props.force_host {
632             &*self.config.host
633         } else {
634             &*self.config.target
635         };
636
637         let aux_dir = self.aux_output_dir_name();
638
639         rustc
640             .arg("-")
641             .arg("-Zno-codegen")
642             .arg("--out-dir")
643             .arg(&out_dir)
644             .arg(&format!("--target={}", target))
645             .arg("-L")
646             .arg(&self.config.build_base)
647             .arg("-L")
648             .arg(aux_dir);
649         self.set_revision_flags(&mut rustc);
650         self.maybe_add_external_args(&mut rustc,
651                                      self.split_maybe_args(&self.config.target_rustcflags));
652         rustc.args(&self.props.compile_flags);
653
654         self.compose_and_run_compiler(rustc, Some(src))
655     }
656
657     fn run_debuginfo_gdb_test(&self) {
658         assert!(self.revision.is_none(), "revisions not relevant here");
659
660         let config = Config {
661             target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
662             host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
663             mode: DebugInfoGdb,
664             ..self.config.clone()
665         };
666
667         let test_cx = TestCx {
668             config: &config,
669             ..*self
670         };
671
672         test_cx.run_debuginfo_gdb_test_no_opt();
673     }
674
675     fn run_debuginfo_gdb_test_no_opt(&self) {
676         let prefixes = if self.config.gdb_native_rust {
677             // GDB with Rust
678             static PREFIXES: &'static [&'static str] = &["gdb", "gdbr"];
679             println!("NOTE: compiletest thinks it is using GDB with native rust support");
680             PREFIXES
681         } else {
682             // Generic GDB
683             static PREFIXES: &'static [&'static str] = &["gdb", "gdbg"];
684             println!("NOTE: compiletest thinks it is using GDB without native rust support");
685             PREFIXES
686         };
687
688         let DebuggerCommands {
689             commands,
690             check_lines,
691             breakpoint_lines,
692         } = self.parse_debugger_commands(prefixes);
693         let mut cmds = commands.join("\n");
694
695         // compile test file (it should have 'compile-flags:-g' in the header)
696         let compiler_run_result = self.compile_test();
697         if !compiler_run_result.status.success() {
698             self.fatal_proc_rec("compilation failed!", &compiler_run_result);
699         }
700
701         let exe_file = self.make_exe_name();
702
703         let debugger_run_result;
704         if is_android_gdb_target(&self.config.target) {
705             cmds = cmds.replace("run", "continue");
706
707             let tool_path = match self.config.android_cross_path.to_str() {
708                 Some(x) => x.to_owned(),
709                 None => self.fatal("cannot find android cross path"),
710             };
711
712             // write debugger script
713             let mut script_str = String::with_capacity(2048);
714             script_str.push_str(&format!("set charset {}\n", Self::charset()));
715             script_str.push_str(&format!("set sysroot {}\n", tool_path));
716             script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
717             script_str.push_str("target remote :5039\n");
718             script_str.push_str(&format!(
719                 "set solib-search-path \
720                  ./{}/stage2/lib/rustlib/{}/lib/\n",
721                 self.config.host, self.config.target
722             ));
723             for line in &breakpoint_lines {
724                 script_str.push_str(
725                     &format!(
726                         "break {:?}:{}\n",
727                         self.testpaths.file.file_name().unwrap().to_string_lossy(),
728                         *line
729                     )[..],
730                 );
731             }
732             script_str.push_str(&cmds);
733             script_str.push_str("\nquit\n");
734
735             debug!("script_str = {}", script_str);
736             self.dump_output_file(&script_str, "debugger.script");
737
738             let adb_path = &self.config.adb_path;
739
740             Command::new(adb_path)
741                 .arg("push")
742                 .arg(&exe_file)
743                 .arg(&self.config.adb_test_dir)
744                 .status()
745                 .expect(&format!("failed to exec `{:?}`", adb_path));
746
747             Command::new(adb_path)
748                 .args(&["forward", "tcp:5039", "tcp:5039"])
749                 .status()
750                 .expect(&format!("failed to exec `{:?}`", adb_path));
751
752             let adb_arg = format!(
753                 "export LD_LIBRARY_PATH={}; \
754                  gdbserver{} :5039 {}/{}",
755                 self.config.adb_test_dir.clone(),
756                 if self.config.target.contains("aarch64") {
757                     "64"
758                 } else {
759                     ""
760                 },
761                 self.config.adb_test_dir.clone(),
762                 exe_file.file_name().unwrap().to_str().unwrap()
763             );
764
765             debug!("adb arg: {}", adb_arg);
766             let mut adb = Command::new(adb_path)
767                 .args(&["shell", &adb_arg])
768                 .stdout(Stdio::piped())
769                 .stderr(Stdio::inherit())
770                 .spawn()
771                 .expect(&format!("failed to exec `{:?}`", adb_path));
772
773             // Wait for the gdbserver to print out "Listening on port ..."
774             // at which point we know that it's started and then we can
775             // execute the debugger below.
776             let mut stdout = BufReader::new(adb.stdout.take().unwrap());
777             let mut line = String::new();
778             loop {
779                 line.truncate(0);
780                 stdout.read_line(&mut line).unwrap();
781                 if line.starts_with("Listening on port 5039") {
782                     break;
783                 }
784             }
785             drop(stdout);
786
787             let mut debugger_script = OsString::from("-command=");
788             debugger_script.push(self.make_out_name("debugger.script"));
789             let debugger_opts: &[&OsStr] = &[
790                 "-quiet".as_ref(),
791                 "-batch".as_ref(),
792                 "-nx".as_ref(),
793                 &debugger_script,
794             ];
795
796             let gdb_path = self.config.gdb.as_ref().unwrap();
797             let Output {
798                 status,
799                 stdout,
800                 stderr,
801             } = Command::new(&gdb_path)
802                 .args(debugger_opts)
803                 .output()
804                 .expect(&format!("failed to exec `{:?}`", gdb_path));
805             let cmdline = {
806                 let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
807                 gdb.args(debugger_opts);
808                 let cmdline = self.make_cmdline(&gdb, "");
809                 logv(self.config, format!("executing {}", cmdline));
810                 cmdline
811             };
812
813             debugger_run_result = ProcRes {
814                 status,
815                 stdout: String::from_utf8(stdout).unwrap(),
816                 stderr: String::from_utf8(stderr).unwrap(),
817                 cmdline,
818             };
819             if adb.kill().is_err() {
820                 println!("Adb process is already finished.");
821             }
822         } else {
823             let rust_src_root = self
824                 .config
825                 .find_rust_src_root()
826                 .expect("Could not find Rust source root");
827             let rust_pp_module_rel_path = Path::new("./src/etc");
828             let rust_pp_module_abs_path = rust_src_root
829                 .join(rust_pp_module_rel_path)
830                 .to_str()
831                 .unwrap()
832                 .to_owned();
833             // write debugger script
834             let mut script_str = String::with_capacity(2048);
835             script_str.push_str(&format!("set charset {}\n", Self::charset()));
836             script_str.push_str("show version\n");
837
838             match self.config.gdb_version {
839                 Some(version) => {
840                     println!(
841                         "NOTE: compiletest thinks it is using GDB version {}",
842                         version
843                     );
844
845                     if version > extract_gdb_version("7.4").unwrap() {
846                         // Add the directory containing the pretty printers to
847                         // GDB's script auto loading safe path
848                         script_str.push_str(&format!(
849                             "add-auto-load-safe-path {}\n",
850                             rust_pp_module_abs_path.replace(r"\", r"\\")
851                         ));
852                     }
853                 }
854                 _ => {
855                     println!(
856                         "NOTE: compiletest does not know which version of \
857                          GDB it is using"
858                     );
859                 }
860             }
861
862             // The following line actually doesn't have to do anything with
863             // pretty printing, it just tells GDB to print values on one line:
864             script_str.push_str("set print pretty off\n");
865
866             // Add the pretty printer directory to GDB's source-file search path
867             script_str.push_str(&format!("directory {}\n", rust_pp_module_abs_path));
868
869             // Load the target executable
870             script_str.push_str(&format!(
871                 "file {}\n",
872                 exe_file.to_str().unwrap().replace(r"\", r"\\")
873             ));
874
875             // Force GDB to print values in the Rust format.
876             if self.config.gdb_native_rust {
877                 script_str.push_str("set language rust\n");
878             }
879
880             // Add line breakpoints
881             for line in &breakpoint_lines {
882                 script_str.push_str(&format!(
883                     "break '{}':{}\n",
884                     self.testpaths.file.file_name().unwrap().to_string_lossy(),
885                     *line
886                 ));
887             }
888
889             script_str.push_str(&cmds);
890             script_str.push_str("\nquit\n");
891
892             debug!("script_str = {}", script_str);
893             self.dump_output_file(&script_str, "debugger.script");
894
895             let mut debugger_script = OsString::from("-command=");
896             debugger_script.push(self.make_out_name("debugger.script"));
897
898             let debugger_opts: &[&OsStr] = &[
899                 "-quiet".as_ref(),
900                 "-batch".as_ref(),
901                 "-nx".as_ref(),
902                 &debugger_script,
903             ];
904
905             let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
906             gdb.args(debugger_opts)
907                 .env("PYTHONPATH", rust_pp_module_abs_path);
908
909             debugger_run_result = self.compose_and_run(
910                 gdb,
911                 self.config.run_lib_path.to_str().unwrap(),
912                 None,
913                 None,
914             );
915         }
916
917         if !debugger_run_result.status.success() {
918             self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
919         }
920
921         self.check_debugger_output(&debugger_run_result, &check_lines);
922     }
923
924     fn run_debuginfo_lldb_test(&self) {
925         assert!(self.revision.is_none(), "revisions not relevant here");
926
927         if self.config.lldb_python_dir.is_none() {
928             self.fatal("Can't run LLDB test because LLDB's python path is not set.");
929         }
930
931         let config = Config {
932             target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
933             host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
934             mode: DebugInfoLldb,
935             ..self.config.clone()
936         };
937
938         let test_cx = TestCx {
939             config: &config,
940             ..*self
941         };
942
943         test_cx.run_debuginfo_lldb_test_no_opt();
944     }
945
946     fn run_debuginfo_lldb_test_no_opt(&self) {
947         // compile test file (it should have 'compile-flags:-g' in the header)
948         let compile_result = self.compile_test();
949         if !compile_result.status.success() {
950             self.fatal_proc_rec("compilation failed!", &compile_result);
951         }
952
953         let exe_file = self.make_exe_name();
954
955         match self.config.lldb_version {
956             Some(ref version) => {
957                 println!(
958                     "NOTE: compiletest thinks it is using LLDB version {}",
959                     version
960                 );
961             }
962             _ => {
963                 println!(
964                     "NOTE: compiletest does not know which version of \
965                      LLDB it is using"
966                 );
967             }
968         }
969
970         let prefixes = if self.config.lldb_native_rust {
971             static PREFIXES: &'static [&'static str] = &["lldb", "lldbr"];
972             println!("NOTE: compiletest thinks it is using LLDB with native rust support");
973             PREFIXES
974         } else {
975             static PREFIXES: &'static [&'static str] = &["lldb", "lldbg"];
976             println!("NOTE: compiletest thinks it is using LLDB without native rust support");
977             PREFIXES
978         };
979
980         // Parse debugger commands etc from test files
981         let DebuggerCommands {
982             commands,
983             check_lines,
984             breakpoint_lines,
985             ..
986         } = self.parse_debugger_commands(prefixes);
987
988         // Write debugger script:
989         // We don't want to hang when calling `quit` while the process is still running
990         let mut script_str = String::from("settings set auto-confirm true\n");
991
992         // Make LLDB emit its version, so we have it documented in the test output
993         script_str.push_str("version\n");
994
995         // Switch LLDB into "Rust mode"
996         let rust_src_root = self
997             .config
998             .find_rust_src_root()
999             .expect("Could not find Rust source root");
1000         let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py");
1001         let rust_pp_module_abs_path = rust_src_root
1002             .join(rust_pp_module_rel_path)
1003             .to_str()
1004             .unwrap()
1005             .to_owned();
1006
1007         script_str
1008             .push_str(&format!("command script import {}\n", &rust_pp_module_abs_path[..])[..]);
1009         script_str.push_str("type summary add --no-value ");
1010         script_str.push_str("--python-function lldb_rust_formatters.print_val ");
1011         script_str.push_str("-x \".*\" --category Rust\n");
1012         script_str.push_str("type category enable Rust\n");
1013
1014         // Set breakpoints on every line that contains the string "#break"
1015         let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
1016         for line in &breakpoint_lines {
1017             script_str.push_str(&format!(
1018                 "breakpoint set --file '{}' --line {}\n",
1019                 source_file_name, line
1020             ));
1021         }
1022
1023         // Append the other commands
1024         for line in &commands {
1025             script_str.push_str(line);
1026             script_str.push_str("\n");
1027         }
1028
1029         // Finally, quit the debugger
1030         script_str.push_str("\nquit\n");
1031
1032         // Write the script into a file
1033         debug!("script_str = {}", script_str);
1034         self.dump_output_file(&script_str, "debugger.script");
1035         let debugger_script = self.make_out_name("debugger.script");
1036
1037         // Let LLDB execute the script via lldb_batchmode.py
1038         let debugger_run_result = self.run_lldb(&exe_file, &debugger_script, &rust_src_root);
1039
1040         if !debugger_run_result.status.success() {
1041             self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
1042         }
1043
1044         self.check_debugger_output(&debugger_run_result, &check_lines);
1045     }
1046
1047     fn run_lldb(
1048         &self,
1049         test_executable: &Path,
1050         debugger_script: &Path,
1051         rust_src_root: &Path,
1052     ) -> ProcRes {
1053         // Prepare the lldb_batchmode which executes the debugger script
1054         let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
1055         self.cmd2procres(
1056             Command::new(&self.config.lldb_python)
1057                 .arg(&lldb_script_path)
1058                 .arg(test_executable)
1059                 .arg(debugger_script)
1060                 .env("PYTHONPATH", self.config.lldb_python_dir.as_ref().unwrap()),
1061         )
1062     }
1063
1064     fn cmd2procres(&self, cmd: &mut Command) -> ProcRes {
1065         let (status, out, err) = match cmd.output() {
1066             Ok(Output {
1067                 status,
1068                 stdout,
1069                 stderr,
1070             }) => (
1071                 status,
1072                 String::from_utf8(stdout).unwrap(),
1073                 String::from_utf8(stderr).unwrap(),
1074             ),
1075             Err(e) => self.fatal(&format!(
1076                 "Failed to setup Python process for \
1077                  LLDB script: {}",
1078                 e
1079             )),
1080         };
1081
1082         self.dump_output(&out, &err);
1083         ProcRes {
1084             status,
1085             stdout: out,
1086             stderr: err,
1087             cmdline: format!("{:?}", cmd),
1088         }
1089     }
1090
1091     fn parse_debugger_commands(&self, debugger_prefixes: &[&str]) -> DebuggerCommands {
1092         let directives = debugger_prefixes
1093             .iter()
1094             .map(|prefix| (format!("{}-command", prefix), format!("{}-check", prefix)))
1095             .collect::<Vec<_>>();
1096
1097         let mut breakpoint_lines = vec![];
1098         let mut commands = vec![];
1099         let mut check_lines = vec![];
1100         let mut counter = 1;
1101         let reader = BufReader::new(File::open(&self.testpaths.file).unwrap());
1102         for line in reader.lines() {
1103             match line {
1104                 Ok(line) => {
1105                     let line = if line.starts_with("//") {
1106                         line[2..].trim_start()
1107                     } else {
1108                         line.as_str()
1109                     };
1110
1111                     if line.contains("#break") {
1112                         breakpoint_lines.push(counter);
1113                     }
1114
1115                     for &(ref command_directive, ref check_directive) in &directives {
1116                         self.config
1117                             .parse_name_value_directive(&line, command_directive)
1118                             .map(|cmd| commands.push(cmd));
1119
1120                         self.config
1121                             .parse_name_value_directive(&line, check_directive)
1122                             .map(|cmd| check_lines.push(cmd));
1123                     }
1124                 }
1125                 Err(e) => self.fatal(&format!("Error while parsing debugger commands: {}", e)),
1126             }
1127             counter += 1;
1128         }
1129
1130         DebuggerCommands {
1131             commands,
1132             check_lines,
1133             breakpoint_lines,
1134         }
1135     }
1136
1137     fn cleanup_debug_info_options(&self, options: &Option<String>) -> Option<String> {
1138         if options.is_none() {
1139             return None;
1140         }
1141
1142         // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
1143         let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()];
1144         let new_options = self
1145             .split_maybe_args(options)
1146             .into_iter()
1147             .filter(|x| !options_to_remove.contains(x))
1148             .collect::<Vec<String>>();
1149
1150         Some(new_options.join(" "))
1151     }
1152
1153     fn maybe_add_external_args(&self, cmd: &mut Command, args: Vec<String>) {
1154         // Filter out the arguments that should not be added by runtest here.
1155         //
1156         // Notable use-cases are: do not add our optimisation flag if
1157         // `compile-flags: -Copt-level=x` and similar for debug-info level as well.
1158         const OPT_FLAGS: &[&str] = &["-O", "-Copt-level=", /*-C<space>*/"opt-level="];
1159         const DEBUG_FLAGS: &[&str] = &["-g", "-Cdebuginfo=", /*-C<space>*/"debuginfo="];
1160
1161         // FIXME: ideally we would "just" check the `cmd` itself, but it does not allow inspecting
1162         // its arguments. They need to be collected separately. For now I cannot be bothered to
1163         // implement this the "right" way.
1164         let have_opt_flag = self.props.compile_flags.iter().any(|arg| {
1165             OPT_FLAGS.iter().any(|f| arg.starts_with(f))
1166         });
1167         let have_debug_flag = self.props.compile_flags.iter().any(|arg| {
1168             DEBUG_FLAGS.iter().any(|f| arg.starts_with(f))
1169         });
1170
1171         for arg in args {
1172             if OPT_FLAGS.iter().any(|f| arg.starts_with(f)) && have_opt_flag {
1173                 continue;
1174             }
1175             if DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)) && have_debug_flag {
1176                 continue;
1177             }
1178             cmd.arg(arg);
1179         }
1180     }
1181
1182     fn check_debugger_output(&self, debugger_run_result: &ProcRes, check_lines: &[String]) {
1183         let num_check_lines = check_lines.len();
1184
1185         let mut check_line_index = 0;
1186         for line in debugger_run_result.stdout.lines() {
1187             if check_line_index >= num_check_lines {
1188                 break;
1189             }
1190
1191             if check_single_line(line, &(check_lines[check_line_index])[..]) {
1192                 check_line_index += 1;
1193             }
1194         }
1195         if check_line_index != num_check_lines && num_check_lines > 0 {
1196             self.fatal_proc_rec(
1197                 &format!(
1198                     "line not found in debugger output: {}",
1199                     check_lines[check_line_index]
1200                 ),
1201                 debugger_run_result,
1202             );
1203         }
1204
1205         fn check_single_line(line: &str, check_line: &str) -> bool {
1206             // Allow check lines to leave parts unspecified (e.g., uninitialized
1207             // bits in the  wrong case of an enum) with the notation "[...]".
1208             let line = line.trim();
1209             let check_line = check_line.trim();
1210             let can_start_anywhere = check_line.starts_with("[...]");
1211             let can_end_anywhere = check_line.ends_with("[...]");
1212
1213             let check_fragments: Vec<&str> = check_line
1214                 .split("[...]")
1215                 .filter(|frag| !frag.is_empty())
1216                 .collect();
1217             if check_fragments.is_empty() {
1218                 return true;
1219             }
1220
1221             let (mut rest, first_fragment) = if can_start_anywhere {
1222                 match line.find(check_fragments[0]) {
1223                     Some(pos) => (&line[pos + check_fragments[0].len()..], 1),
1224                     None => return false,
1225                 }
1226             } else {
1227                 (line, 0)
1228             };
1229
1230             for current_fragment in &check_fragments[first_fragment..] {
1231                 match rest.find(current_fragment) {
1232                     Some(pos) => {
1233                         rest = &rest[pos + current_fragment.len()..];
1234                     }
1235                     None => return false,
1236                 }
1237             }
1238
1239             if !can_end_anywhere && !rest.is_empty() {
1240                 return false;
1241             }
1242
1243             true
1244         }
1245     }
1246
1247     fn check_error_patterns(&self, output_to_check: &str, proc_res: &ProcRes) {
1248         debug!("check_error_patterns");
1249         if self.props.error_patterns.is_empty() {
1250             if self.props.compile_pass {
1251                 return;
1252             } else {
1253                 self.fatal(&format!(
1254                     "no error pattern specified in {:?}",
1255                     self.testpaths.file.display()
1256                 ));
1257             }
1258         }
1259
1260         let mut missing_patterns: Vec<String> = Vec::new();
1261
1262         for pattern in &self.props.error_patterns {
1263             if output_to_check.contains(pattern.trim()) {
1264                 debug!("found error pattern {}", pattern);
1265             } else {
1266                 missing_patterns.push(pattern.to_string());
1267             }
1268         }
1269
1270         if missing_patterns.is_empty() {
1271             return;
1272         }
1273
1274         if missing_patterns.len() == 1 {
1275             self.fatal_proc_rec(
1276                 &format!("error pattern '{}' not found!", missing_patterns[0]),
1277                 proc_res,
1278             );
1279         } else {
1280             for pattern in missing_patterns {
1281                 self.error(&format!("error pattern '{}' not found!", pattern));
1282             }
1283             self.fatal_proc_rec("multiple error patterns not found", proc_res);
1284         }
1285     }
1286
1287     fn check_no_compiler_crash(&self, proc_res: &ProcRes) {
1288         match proc_res.status.code() {
1289             Some(101) => self.fatal_proc_rec("compiler encountered internal error", proc_res),
1290             None => self.fatal_proc_rec("compiler terminated by signal", proc_res),
1291             _ => (),
1292         }
1293     }
1294
1295     fn check_forbid_output(&self, output_to_check: &str, proc_res: &ProcRes) {
1296         for pat in &self.props.forbid_output {
1297             if output_to_check.contains(pat) {
1298                 self.fatal_proc_rec("forbidden pattern found in compiler output", proc_res);
1299             }
1300         }
1301     }
1302
1303     fn check_expected_errors(&self, expected_errors: Vec<errors::Error>, proc_res: &ProcRes) {
1304         debug!("check_expected_errors: expected_errors={:?} proc_res.status={:?}",
1305                expected_errors, proc_res.status);
1306         if proc_res.status.success()
1307             && expected_errors
1308                 .iter()
1309                 .any(|x| x.kind == Some(ErrorKind::Error))
1310         {
1311             self.fatal_proc_rec("process did not return an error status", proc_res);
1312         }
1313
1314         // On Windows, keep all '\' path separators to match the paths reported in the JSON output
1315         // from the compiler
1316         let os_file_name = self.testpaths.file.display().to_string();
1317
1318         // on windows, translate all '\' path separators to '/'
1319         let file_name = format!("{}", self.testpaths.file.display()).replace(r"\", "/");
1320
1321         // If the testcase being checked contains at least one expected "help"
1322         // message, then we'll ensure that all "help" messages are expected.
1323         // Otherwise, all "help" messages reported by the compiler will be ignored.
1324         // This logic also applies to "note" messages.
1325         let expect_help = expected_errors
1326             .iter()
1327             .any(|ee| ee.kind == Some(ErrorKind::Help));
1328         let expect_note = expected_errors
1329             .iter()
1330             .any(|ee| ee.kind == Some(ErrorKind::Note));
1331
1332         // Parse the JSON output from the compiler and extract out the messages.
1333         let actual_errors = json::parse_output(&os_file_name, &proc_res.stderr, proc_res);
1334         let mut unexpected = Vec::new();
1335         let mut found = vec![false; expected_errors.len()];
1336         for actual_error in &actual_errors {
1337             let opt_index = expected_errors.iter().enumerate().position(
1338                 |(index, expected_error)| {
1339                     !found[index] && actual_error.line_num == expected_error.line_num
1340                         && (expected_error.kind.is_none()
1341                             || actual_error.kind == expected_error.kind)
1342                         && actual_error.msg.contains(&expected_error.msg)
1343                 },
1344             );
1345
1346             match opt_index {
1347                 Some(index) => {
1348                     // found a match, everybody is happy
1349                     assert!(!found[index]);
1350                     found[index] = true;
1351                 }
1352
1353                 None => {
1354                     if self.is_unexpected_compiler_message(actual_error, expect_help, expect_note) {
1355                         self.error(&format!(
1356                             "{}:{}: unexpected {}: '{}'",
1357                             file_name,
1358                             actual_error.line_num,
1359                             actual_error
1360                                 .kind
1361                                 .as_ref()
1362                                 .map_or(String::from("message"), |k| k.to_string()),
1363                             actual_error.msg
1364                         ));
1365                         unexpected.push(actual_error);
1366                     }
1367                 }
1368             }
1369         }
1370
1371         let mut not_found = Vec::new();
1372         // anything not yet found is a problem
1373         for (index, expected_error) in expected_errors.iter().enumerate() {
1374             if !found[index] {
1375                 self.error(&format!(
1376                     "{}:{}: expected {} not found: {}",
1377                     file_name,
1378                     expected_error.line_num,
1379                     expected_error
1380                         .kind
1381                         .as_ref()
1382                         .map_or("message".into(), |k| k.to_string()),
1383                     expected_error.msg
1384                 ));
1385                 not_found.push(expected_error);
1386             }
1387         }
1388
1389         if !unexpected.is_empty() || !not_found.is_empty() {
1390             self.error(&format!(
1391                 "{} unexpected errors found, {} expected errors not found",
1392                 unexpected.len(),
1393                 not_found.len()
1394             ));
1395             println!("status: {}\ncommand: {}", proc_res.status, proc_res.cmdline);
1396             if !unexpected.is_empty() {
1397                 println!("unexpected errors (from JSON output): {:#?}\n", unexpected);
1398             }
1399             if !not_found.is_empty() {
1400                 println!("not found errors (from test file): {:#?}\n", not_found);
1401             }
1402             panic!();
1403         }
1404     }
1405
1406     /// Returns `true` if we should report an error about `actual_error`,
1407     /// which did not match any of the expected error. We always require
1408     /// errors/warnings to be explicitly listed, but only require
1409     /// helps/notes if there are explicit helps/notes given.
1410     fn is_unexpected_compiler_message(
1411         &self,
1412         actual_error: &Error,
1413         expect_help: bool,
1414         expect_note: bool,
1415     ) -> bool {
1416         match actual_error.kind {
1417             Some(ErrorKind::Help) => expect_help,
1418             Some(ErrorKind::Note) => expect_note,
1419             Some(ErrorKind::Error) | Some(ErrorKind::Warning) => true,
1420             Some(ErrorKind::Suggestion) | None => false,
1421         }
1422     }
1423
1424     fn compile_test(&self) -> ProcRes {
1425         let mut rustc = self.make_compile_args(
1426             &self.testpaths.file,
1427             TargetLocation::ThisFile(self.make_exe_name()),
1428         );
1429
1430         rustc.arg("-L").arg(&self.aux_output_dir_name());
1431
1432         match self.config.mode {
1433             CompileFail | Ui => {
1434                 // compile-fail and ui tests tend to have tons of unused code as
1435                 // it's just testing various pieces of the compile, but we don't
1436                 // want to actually assert warnings about all this code. Instead
1437                 // let's just ignore unused code warnings by defaults and tests
1438                 // can turn it back on if needed.
1439                 if !self.config.src_base.ends_with("rustdoc-ui") {
1440                     rustc.args(&["-A", "unused"]);
1441                 }
1442             }
1443             _ => {}
1444         }
1445
1446         self.compose_and_run_compiler(rustc, None)
1447     }
1448
1449     fn document(&self, out_dir: &Path) -> ProcRes {
1450         if self.props.build_aux_docs {
1451             for rel_ab in &self.props.aux_builds {
1452                 let aux_testpaths = self.compute_aux_test_paths(rel_ab);
1453                 let aux_props =
1454                     self.props
1455                         .from_aux_file(&aux_testpaths.file, self.revision, self.config);
1456                 let aux_cx = TestCx {
1457                     config: self.config,
1458                     props: &aux_props,
1459                     testpaths: &aux_testpaths,
1460                     revision: self.revision,
1461                 };
1462                 // Create the directory for the stdout/stderr files.
1463                 create_dir_all(aux_cx.output_base_dir()).unwrap();
1464                 let auxres = aux_cx.document(out_dir);
1465                 if !auxres.status.success() {
1466                     return auxres;
1467                 }
1468             }
1469         }
1470
1471         let aux_dir = self.aux_output_dir_name();
1472
1473         let rustdoc_path = self
1474             .config
1475             .rustdoc_path
1476             .as_ref()
1477             .expect("--rustdoc-path passed");
1478         let mut rustdoc = Command::new(rustdoc_path);
1479
1480         rustdoc
1481             .arg("-L")
1482             .arg(self.config.run_lib_path.to_str().unwrap())
1483             .arg("-L")
1484             .arg(aux_dir)
1485             .arg("-o")
1486             .arg(out_dir)
1487             .arg(&self.testpaths.file)
1488             .args(&self.props.compile_flags);
1489
1490         if let Some(ref linker) = self.config.linker {
1491             rustdoc
1492                 .arg("--linker")
1493                 .arg(linker)
1494                 .arg("-Z")
1495                 .arg("unstable-options");
1496         }
1497
1498         self.compose_and_run_compiler(rustdoc, None)
1499     }
1500
1501     fn exec_compiled_test(&self) -> ProcRes {
1502         let env = &self.props.exec_env;
1503
1504         let proc_res = match &*self.config.target {
1505             // This is pretty similar to below, we're transforming:
1506             //
1507             //      program arg1 arg2
1508             //
1509             // into
1510             //
1511             //      remote-test-client run program:support-lib.so arg1 arg2
1512             //
1513             // The test-client program will upload `program` to the emulator
1514             // along with all other support libraries listed (in this case
1515             // `support-lib.so`. It will then execute the program on the
1516             // emulator with the arguments specified (in the environment we give
1517             // the process) and then report back the same result.
1518             _ if self.config.remote_test_client.is_some() => {
1519                 let aux_dir = self.aux_output_dir_name();
1520                 let ProcArgs { mut prog, args } = self.make_run_args();
1521                 if let Ok(entries) = aux_dir.read_dir() {
1522                     for entry in entries {
1523                         let entry = entry.unwrap();
1524                         if !entry.path().is_file() {
1525                             continue;
1526                         }
1527                         prog.push_str(":");
1528                         prog.push_str(entry.path().to_str().unwrap());
1529                     }
1530                 }
1531                 let mut test_client =
1532                     Command::new(self.config.remote_test_client.as_ref().unwrap());
1533                 test_client
1534                     .args(&["run", &prog])
1535                     .args(args)
1536                     .envs(env.clone());
1537                 self.compose_and_run(
1538                     test_client,
1539                     self.config.run_lib_path.to_str().unwrap(),
1540                     Some(aux_dir.to_str().unwrap()),
1541                     None,
1542                 )
1543             }
1544             _ => {
1545                 let aux_dir = self.aux_output_dir_name();
1546                 let ProcArgs { prog, args } = self.make_run_args();
1547                 let mut program = Command::new(&prog);
1548                 program
1549                     .args(args)
1550                     .current_dir(&self.output_base_dir())
1551                     .envs(env.clone());
1552                 self.compose_and_run(
1553                     program,
1554                     self.config.run_lib_path.to_str().unwrap(),
1555                     Some(aux_dir.to_str().unwrap()),
1556                     None,
1557                 )
1558             }
1559         };
1560
1561         if proc_res.status.success() {
1562             // delete the executable after running it to save space.
1563             // it is ok if the deletion failed.
1564             let _ = fs::remove_file(self.make_exe_name());
1565         }
1566
1567         proc_res
1568     }
1569
1570     /// For each `aux-build: foo/bar` annotation, we check to find the
1571     /// file in a `auxiliary` directory relative to the test itself.
1572     fn compute_aux_test_paths(&self, rel_ab: &str) -> TestPaths {
1573         let test_ab = self
1574             .testpaths
1575             .file
1576             .parent()
1577             .expect("test file path has no parent")
1578             .join("auxiliary")
1579             .join(rel_ab);
1580         if !test_ab.exists() {
1581             self.fatal(&format!(
1582                 "aux-build `{}` source not found",
1583                 test_ab.display()
1584             ))
1585         }
1586
1587         TestPaths {
1588             file: test_ab,
1589             relative_dir: self
1590                 .testpaths
1591                 .relative_dir
1592                 .join(self.output_testname_unique())
1593                 .join("auxiliary")
1594                 .join(rel_ab)
1595                 .parent()
1596                 .expect("aux-build path has no parent")
1597                 .to_path_buf(),
1598         }
1599     }
1600
1601     fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
1602         let aux_dir = self.aux_output_dir_name();
1603
1604         if !self.props.aux_builds.is_empty() {
1605             let _ = fs::remove_dir_all(&aux_dir);
1606             create_dir_all(&aux_dir).unwrap();
1607         }
1608
1609         // Use a Vec instead of a HashMap to preserve original order
1610         let mut extern_priv = self.props.extern_private.clone();
1611
1612         let mut add_extern_priv = |priv_dep: &str, dylib: bool| {
1613             let lib_name = get_lib_name(priv_dep, dylib);
1614             rustc
1615                 .arg("--extern-private")
1616                 .arg(format!("{}={}", priv_dep, aux_dir.join(lib_name).to_str().unwrap()));
1617         };
1618
1619         for rel_ab in &self.props.aux_builds {
1620             let aux_testpaths = self.compute_aux_test_paths(rel_ab);
1621             let aux_props =
1622                 self.props
1623                     .from_aux_file(&aux_testpaths.file, self.revision, self.config);
1624             let aux_output = TargetLocation::ThisDirectory(self.aux_output_dir_name());
1625             let aux_cx = TestCx {
1626                 config: self.config,
1627                 props: &aux_props,
1628                 testpaths: &aux_testpaths,
1629                 revision: self.revision,
1630             };
1631             // Create the directory for the stdout/stderr files.
1632             create_dir_all(aux_cx.output_base_dir()).unwrap();
1633             let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output);
1634
1635             let (dylib, crate_type) = if aux_props.no_prefer_dynamic {
1636                 (true, None)
1637             } else if self.config.target.contains("cloudabi")
1638                 || self.config.target.contains("emscripten")
1639                 || (self.config.target.contains("musl") && !aux_props.force_host)
1640                 || self.config.target.contains("wasm32")
1641                 || self.config.target.contains("nvptx")
1642             {
1643                 // We primarily compile all auxiliary libraries as dynamic libraries
1644                 // to avoid code size bloat and large binaries as much as possible
1645                 // for the test suite (otherwise including libstd statically in all
1646                 // executables takes up quite a bit of space).
1647                 //
1648                 // For targets like MUSL or Emscripten, however, there is no support for
1649                 // dynamic libraries so we just go back to building a normal library. Note,
1650                 // however, that for MUSL if the library is built with `force_host` then
1651                 // it's ok to be a dylib as the host should always support dylibs.
1652                 (false, Some("lib"))
1653             } else {
1654                 (true, Some("dylib"))
1655             };
1656
1657             let trimmed = rel_ab.trim_end_matches(".rs").to_string();
1658
1659             // Normally, every 'extern-private' has a correspodning 'aux-build'
1660             // entry. If so, we remove it from our list of private crates,
1661             // and add an '--extern-private' flag to rustc
1662             if extern_priv.remove_item(&trimmed).is_some() {
1663                 add_extern_priv(&trimmed, dylib);
1664             }
1665
1666             if let Some(crate_type) = crate_type {
1667                 aux_rustc.args(&["--crate-type", crate_type]);
1668             }
1669
1670             aux_rustc.arg("-L").arg(&aux_dir);
1671
1672             let auxres = aux_cx.compose_and_run(
1673                 aux_rustc,
1674                 aux_cx.config.compile_lib_path.to_str().unwrap(),
1675                 Some(aux_dir.to_str().unwrap()),
1676                 None,
1677             );
1678             if !auxres.status.success() {
1679                 self.fatal_proc_rec(
1680                     &format!(
1681                         "auxiliary build of {:?} failed to compile: ",
1682                         aux_testpaths.file.display()
1683                     ),
1684                     &auxres,
1685                 );
1686             }
1687         }
1688
1689         // Add any '--extern-private' entries without a matching
1690         // 'aux-build'
1691         for private_lib in extern_priv {
1692             add_extern_priv(&private_lib, true);
1693         }
1694
1695         rustc.envs(self.props.rustc_env.clone());
1696         self.compose_and_run(
1697             rustc,
1698             self.config.compile_lib_path.to_str().unwrap(),
1699             Some(aux_dir.to_str().unwrap()),
1700             input,
1701         )
1702     }
1703
1704     fn compose_and_run(
1705         &self,
1706         mut command: Command,
1707         lib_path: &str,
1708         aux_path: Option<&str>,
1709         input: Option<String>,
1710     ) -> ProcRes {
1711         let cmdline = {
1712             let cmdline = self.make_cmdline(&command, lib_path);
1713             logv(self.config, format!("executing {}", cmdline));
1714             cmdline
1715         };
1716
1717         command
1718             .stdout(Stdio::piped())
1719             .stderr(Stdio::piped())
1720             .stdin(Stdio::piped());
1721
1722         // Need to be sure to put both the lib_path and the aux path in the dylib
1723         // search path for the child.
1724         let mut path = env::split_paths(&env::var_os(dylib_env_var()).unwrap_or(OsString::new()))
1725             .collect::<Vec<_>>();
1726         if let Some(p) = aux_path {
1727             path.insert(0, PathBuf::from(p))
1728         }
1729         path.insert(0, PathBuf::from(lib_path));
1730
1731         // Add the new dylib search path var
1732         let newpath = env::join_paths(&path).unwrap();
1733         command.env(dylib_env_var(), newpath);
1734
1735         let mut child = disable_error_reporting(|| command.spawn())
1736             .expect(&format!("failed to exec `{:?}`", &command));
1737         if let Some(input) = input {
1738             child
1739                 .stdin
1740                 .as_mut()
1741                 .unwrap()
1742                 .write_all(input.as_bytes())
1743                 .unwrap();
1744         }
1745
1746         let Output {
1747             status,
1748             stdout,
1749             stderr,
1750         } = read2_abbreviated(child).expect("failed to read output");
1751
1752         let result = ProcRes {
1753             status,
1754             stdout: String::from_utf8_lossy(&stdout).into_owned(),
1755             stderr: String::from_utf8_lossy(&stderr).into_owned(),
1756             cmdline,
1757         };
1758
1759         self.dump_output(&result.stdout, &result.stderr);
1760
1761         result
1762     }
1763
1764     fn make_compile_args(&self, input_file: &Path, output_file: TargetLocation) -> Command {
1765         let is_rustdoc = self.config.src_base.ends_with("rustdoc-ui") ||
1766                          self.config.src_base.ends_with("rustdoc-js");
1767         let mut rustc = if !is_rustdoc {
1768             Command::new(&self.config.rustc_path)
1769         } else {
1770             Command::new(
1771                 &self
1772                     .config
1773                     .rustdoc_path
1774                     .clone()
1775                     .expect("no rustdoc built yet"),
1776             )
1777         };
1778         // FIXME Why is -L here?
1779         rustc.arg(input_file); //.arg("-L").arg(&self.config.build_base);
1780
1781         // Use a single thread for efficiency and a deterministic error message order
1782         rustc.arg("-Zthreads=1");
1783
1784         // Optionally prevent default --target if specified in test compile-flags.
1785         let custom_target = self
1786             .props
1787             .compile_flags
1788             .iter()
1789             .any(|x| x.starts_with("--target"));
1790
1791         if !custom_target {
1792             let target = if self.props.force_host {
1793                 &*self.config.host
1794             } else {
1795                 &*self.config.target
1796             };
1797
1798             rustc.arg(&format!("--target={}", target));
1799         }
1800         self.set_revision_flags(&mut rustc);
1801
1802         if !is_rustdoc {
1803             if let Some(ref incremental_dir) = self.props.incremental_dir {
1804                 rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]);
1805                 rustc.args(&["-Z", "incremental-verify-ich"]);
1806                 rustc.args(&["-Z", "incremental-queries"]);
1807             }
1808
1809             if self.config.mode == CodegenUnits {
1810                 rustc.args(&["-Z", "human_readable_cgu_names"]);
1811             }
1812         }
1813
1814         match self.config.mode {
1815             CompileFail | Incremental => {
1816                 // If we are extracting and matching errors in the new
1817                 // fashion, then you want JSON mode. Old-skool error
1818                 // patterns still match the raw compiler output.
1819                 if self.props.error_patterns.is_empty() {
1820                     rustc.args(&["--error-format", "json"]);
1821                 }
1822                 if !self.props.disable_ui_testing_normalization {
1823                     rustc.arg("-Zui-testing");
1824                 }
1825             }
1826             RunPass | Ui => {
1827                 if !self
1828                     .props
1829                     .compile_flags
1830                     .iter()
1831                     .any(|s| s.starts_with("--error-format"))
1832                 {
1833                     rustc.args(&["--error-format", "json"]);
1834                 }
1835                 if !self.props.disable_ui_testing_normalization {
1836                     rustc.arg("-Zui-testing");
1837                 }
1838             }
1839             MirOpt => {
1840                 rustc.args(&[
1841                     "-Zdump-mir=all",
1842                     "-Zmir-opt-level=3",
1843                     "-Zdump-mir-exclude-pass-number",
1844                 ]);
1845
1846                 let mir_dump_dir = self.get_mir_dump_dir();
1847                 let _ = fs::remove_dir_all(&mir_dump_dir);
1848                 create_dir_all(mir_dump_dir.as_path()).unwrap();
1849                 let mut dir_opt = "-Zdump-mir-dir=".to_string();
1850                 dir_opt.push_str(mir_dump_dir.to_str().unwrap());
1851                 debug!("dir_opt: {:?}", dir_opt);
1852
1853                 rustc.arg(dir_opt);
1854             }
1855             RunFail | RunPassValgrind | Pretty | DebugInfoBoth | DebugInfoGdb | DebugInfoLldb
1856             | Codegen | Rustdoc | RunMake | CodegenUnits | JsDocTest | Assembly => {
1857                 // do not use JSON output
1858             }
1859         }
1860
1861         if self.props.skip_codegen {
1862             assert!(
1863                 !self
1864                     .props
1865                     .compile_flags
1866                     .iter()
1867                     .any(|s| s.starts_with("--emit"))
1868             );
1869             rustc.args(&["--emit", "metadata"]);
1870         }
1871
1872         if !is_rustdoc {
1873             if self.config.target == "wasm32-unknown-unknown" {
1874                 // rustc.arg("-g"); // get any backtrace at all on errors
1875             } else if !self.props.no_prefer_dynamic {
1876                 rustc.args(&["-C", "prefer-dynamic"]);
1877             }
1878         }
1879
1880         match output_file {
1881             TargetLocation::ThisFile(path) => {
1882                 rustc.arg("-o").arg(path);
1883             }
1884             TargetLocation::ThisDirectory(path) => {
1885                 rustc.arg("--out-dir").arg(path);
1886             }
1887         }
1888
1889         match self.config.compare_mode {
1890             Some(CompareMode::Nll) => {
1891                 // FIXME(#56993) use -Zborrowck=mir
1892                 rustc.args(&["-Zborrowck=migrate"]);
1893             }
1894             Some(CompareMode::Polonius) => {
1895                 rustc.args(&["-Zpolonius", "-Zborrowck=mir"]);
1896             }
1897             None => {}
1898         }
1899
1900         if self.props.force_host {
1901             self.maybe_add_external_args(&mut rustc,
1902                                          self.split_maybe_args(&self.config.host_rustcflags));
1903         } else {
1904             self.maybe_add_external_args(&mut rustc,
1905                                          self.split_maybe_args(&self.config.target_rustcflags));
1906             if !is_rustdoc {
1907                 if let Some(ref linker) = self.config.linker {
1908                     rustc.arg(format!("-Clinker={}", linker));
1909                 }
1910             }
1911         }
1912
1913         rustc.args(&self.props.compile_flags);
1914
1915         rustc
1916     }
1917
1918     fn make_exe_name(&self) -> PathBuf {
1919         // Using a single letter here to keep the path length down for
1920         // Windows.  Some test names get very long.  rustc creates `rcgu`
1921         // files with the module name appended to it which can more than
1922         // double the length.
1923         let mut f = self.output_base_dir().join("a");
1924         // FIXME: This is using the host architecture exe suffix, not target!
1925         if self.config.target.contains("emscripten") {
1926             f = f.with_extra_extension("js");
1927         } else if self.config.target.contains("wasm32") {
1928             f = f.with_extra_extension("wasm");
1929         } else if !env::consts::EXE_SUFFIX.is_empty() {
1930             f = f.with_extra_extension(env::consts::EXE_SUFFIX);
1931         }
1932         f
1933     }
1934
1935     fn make_run_args(&self) -> ProcArgs {
1936         // If we've got another tool to run under (valgrind),
1937         // then split apart its command
1938         let mut args = self.split_maybe_args(&self.config.runtool);
1939
1940         // If this is emscripten, then run tests under nodejs
1941         if self.config.target.contains("emscripten") {
1942             if let Some(ref p) = self.config.nodejs {
1943                 args.push(p.clone());
1944             } else {
1945                 self.fatal("no NodeJS binary found (--nodejs)");
1946             }
1947         // If this is otherwise wasm, then run tests under nodejs with our
1948         // shim
1949         } else if self.config.target.contains("wasm32") {
1950             if let Some(ref p) = self.config.nodejs {
1951                 args.push(p.clone());
1952             } else {
1953                 self.fatal("no NodeJS binary found (--nodejs)");
1954             }
1955
1956             let src = self.config.src_base
1957                 .parent().unwrap() // chop off `run-pass`
1958                 .parent().unwrap() // chop off `test`
1959                 .parent().unwrap(); // chop off `src`
1960             args.push(src.join("src/etc/wasm32-shim.js").display().to_string());
1961         }
1962
1963         let exe_file = self.make_exe_name();
1964
1965         // FIXME (#9639): This needs to handle non-utf8 paths
1966         args.push(exe_file.to_str().unwrap().to_owned());
1967
1968         // Add the arguments in the run_flags directive
1969         args.extend(self.split_maybe_args(&self.props.run_flags));
1970
1971         let prog = args.remove(0);
1972         ProcArgs { prog, args }
1973     }
1974
1975     fn split_maybe_args(&self, argstr: &Option<String>) -> Vec<String> {
1976         match *argstr {
1977             Some(ref s) => s
1978                 .split(' ')
1979                 .filter_map(|s| {
1980                     if s.chars().all(|c| c.is_whitespace()) {
1981                         None
1982                     } else {
1983                         Some(s.to_owned())
1984                     }
1985                 })
1986                 .collect(),
1987             None => Vec::new(),
1988         }
1989     }
1990
1991     fn make_cmdline(&self, command: &Command, libpath: &str) -> String {
1992         use crate::util;
1993
1994         // Linux and mac don't require adjusting the library search path
1995         if cfg!(unix) {
1996             format!("{:?}", command)
1997         } else {
1998             // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
1999             // for diagnostic purposes
2000             fn lib_path_cmd_prefix(path: &str) -> String {
2001                 format!(
2002                     "{}=\"{}\"",
2003                     util::lib_path_env_var(),
2004                     util::make_new_path(path)
2005                 )
2006             }
2007
2008             format!("{} {:?}", lib_path_cmd_prefix(libpath), command)
2009         }
2010     }
2011
2012     fn dump_output(&self, out: &str, err: &str) {
2013         let revision = if let Some(r) = self.revision {
2014             format!("{}.", r)
2015         } else {
2016             String::new()
2017         };
2018
2019         self.dump_output_file(out, &format!("{}out", revision));
2020         self.dump_output_file(err, &format!("{}err", revision));
2021         self.maybe_dump_to_stdout(out, err);
2022     }
2023
2024     fn dump_output_file(&self, out: &str, extension: &str) {
2025         let outfile = self.make_out_name(extension);
2026         fs::write(&outfile, out).unwrap();
2027     }
2028
2029     /// Creates a filename for output with the given extension.
2030     /// E.g., `/.../testname.revision.mode/testname.extension`.
2031     fn make_out_name(&self, extension: &str) -> PathBuf {
2032         self.output_base_name().with_extension(extension)
2033     }
2034
2035     /// Gets the directory where auxiliary files are written.
2036     /// E.g., `/.../testname.revision.mode/auxiliary/`.
2037     fn aux_output_dir_name(&self) -> PathBuf {
2038         self.output_base_dir()
2039             .join("auxiliary")
2040             .with_extra_extension(self.config.mode.disambiguator())
2041     }
2042
2043     /// Generates a unique name for the test, such as `testname.revision.mode`.
2044     fn output_testname_unique(&self) -> PathBuf {
2045         output_testname_unique(self.config, self.testpaths, self.safe_revision())
2046     }
2047
2048     /// The revision, ignored for incremental compilation since it wants all revisions in
2049     /// the same directory.
2050     fn safe_revision(&self) -> Option<&str> {
2051         if self.config.mode == Incremental {
2052             None
2053         } else {
2054             self.revision
2055         }
2056     }
2057
2058     /// Gets the absolute path to the directory where all output for the given
2059     /// test/revision should reside.
2060     /// E.g., `/path/to/build/host-triple/test/ui/relative/testname.revision.mode/`.
2061     fn output_base_dir(&self) -> PathBuf {
2062         output_base_dir(self.config, self.testpaths, self.safe_revision())
2063     }
2064
2065     /// Gets the absolute path to the base filename used as output for the given
2066     /// test/revision.
2067     /// E.g., `/.../relative/testname.revision.mode/testname`.
2068     fn output_base_name(&self) -> PathBuf {
2069         output_base_name(self.config, self.testpaths, self.safe_revision())
2070     }
2071
2072     fn maybe_dump_to_stdout(&self, out: &str, err: &str) {
2073         if self.config.verbose {
2074             println!("------{}------------------------------", "stdout");
2075             println!("{}", out);
2076             println!("------{}------------------------------", "stderr");
2077             println!("{}", err);
2078             println!("------------------------------------------");
2079         }
2080     }
2081
2082     fn error(&self, err: &str) {
2083         match self.revision {
2084             Some(rev) => println!("\nerror in revision `{}`: {}", rev, err),
2085             None => println!("\nerror: {}", err),
2086         }
2087     }
2088
2089     fn fatal(&self, err: &str) -> ! {
2090         self.error(err);
2091         error!("fatal error, panic: {:?}", err);
2092         panic!("fatal error");
2093     }
2094
2095     fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
2096         self.error(err);
2097         proc_res.fatal(None);
2098     }
2099
2100     // codegen tests (using FileCheck)
2101
2102     fn compile_test_and_save_ir(&self) -> ProcRes {
2103         let aux_dir = self.aux_output_dir_name();
2104
2105         let output_file = TargetLocation::ThisDirectory(self.output_base_dir());
2106         let mut rustc = self.make_compile_args(&self.testpaths.file, output_file);
2107         rustc.arg("-L").arg(aux_dir).arg("--emit=llvm-ir");
2108
2109         self.compose_and_run_compiler(rustc, None)
2110     }
2111
2112     fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
2113         // This works with both `--emit asm` (as default output name for the assembly)
2114         // and `ptx-linker` because the latter can write output at requested location.
2115         let output_path = self.output_base_name().with_extension("s");
2116
2117         let output_file = TargetLocation::ThisFile(output_path.clone());
2118         let mut rustc = self.make_compile_args(&self.testpaths.file, output_file);
2119
2120         rustc.arg("-L").arg(self.aux_output_dir_name());
2121
2122         match self.props.assembly_output.as_ref().map(AsRef::as_ref) {
2123             Some("emit-asm") => {
2124                 rustc.arg("--emit=asm");
2125             }
2126
2127             Some("ptx-linker") => {
2128                 // No extra flags needed.
2129             }
2130
2131             Some(_) => self.fatal("unknown 'assembly-output' header"),
2132             None => self.fatal("missing 'assembly-output' header"),
2133         }
2134
2135         (self.compose_and_run_compiler(rustc, None), output_path)
2136     }
2137
2138     fn verify_with_filecheck(&self, output: &Path) -> ProcRes {
2139         let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
2140         filecheck
2141             .arg("--input-file")
2142             .arg(output)
2143             .arg(&self.testpaths.file);
2144         // It would be more appropriate to make most of the arguments configurable through
2145         // a comment-attribute similar to `compile-flags`. For example, --check-prefixes is a very
2146         // useful flag.
2147         //
2148         // For now, though…
2149         if let Some(rev) = self.revision {
2150             let prefixes = format!("CHECK,{}", rev);
2151             filecheck.args(&["--check-prefixes", &prefixes]);
2152         }
2153         self.compose_and_run(filecheck, "", None, None)
2154     }
2155
2156     fn run_codegen_test(&self) {
2157         if self.config.llvm_filecheck.is_none() {
2158             self.fatal("missing --llvm-filecheck");
2159         }
2160
2161         let proc_res = self.compile_test_and_save_ir();
2162         if !proc_res.status.success() {
2163             self.fatal_proc_rec("compilation failed!", &proc_res);
2164         }
2165
2166         let output_path = self.output_base_name().with_extension("ll");
2167         let proc_res = self.verify_with_filecheck(&output_path);
2168         if !proc_res.status.success() {
2169             self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
2170         }
2171     }
2172
2173     fn run_assembly_test(&self) {
2174         if self.config.llvm_filecheck.is_none() {
2175             self.fatal("missing --llvm-filecheck");
2176         }
2177
2178         let (proc_res, output_path) = self.compile_test_and_save_assembly();
2179         if !proc_res.status.success() {
2180             self.fatal_proc_rec("compilation failed!", &proc_res);
2181         }
2182
2183         let proc_res = self.verify_with_filecheck(&output_path);
2184         if !proc_res.status.success() {
2185             self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
2186         }
2187     }
2188
2189     fn charset() -> &'static str {
2190         // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
2191         if cfg!(target_os = "bitrig") {
2192             "auto"
2193         } else if cfg!(target_os = "freebsd") {
2194             "ISO-8859-1"
2195         } else {
2196             "UTF-8"
2197         }
2198     }
2199
2200     fn run_rustdoc_test(&self) {
2201         assert!(self.revision.is_none(), "revisions not relevant here");
2202
2203         let out_dir = self.output_base_dir();
2204         let _ = fs::remove_dir_all(&out_dir);
2205         create_dir_all(&out_dir).unwrap();
2206
2207         let proc_res = self.document(&out_dir);
2208         if !proc_res.status.success() {
2209             self.fatal_proc_rec("rustdoc failed!", &proc_res);
2210         }
2211
2212         if self.props.check_test_line_numbers_match {
2213             self.check_rustdoc_test_option(proc_res);
2214         } else {
2215             let root = self.config.find_rust_src_root().unwrap();
2216             let res = self.cmd2procres(
2217                 Command::new(&self.config.docck_python)
2218                     .arg(root.join("src/etc/htmldocck.py"))
2219                     .arg(out_dir)
2220                     .arg(&self.testpaths.file),
2221             );
2222             if !res.status.success() {
2223                 self.fatal_proc_rec("htmldocck failed!", &res);
2224             }
2225         }
2226     }
2227
2228     fn get_lines<P: AsRef<Path>>(
2229         &self,
2230         path: &P,
2231         mut other_files: Option<&mut Vec<String>>,
2232     ) -> Vec<usize> {
2233         let content = fs::read_to_string(&path).unwrap();
2234         let mut ignore = false;
2235         content
2236             .lines()
2237             .enumerate()
2238             .filter_map(|(line_nb, line)| {
2239                 if (line.trim_start().starts_with("pub mod ")
2240                     || line.trim_start().starts_with("mod "))
2241                     && line.ends_with(';')
2242                 {
2243                     if let Some(ref mut other_files) = other_files {
2244                         other_files.push(line.rsplit("mod ").next().unwrap().replace(";", ""));
2245                     }
2246                     None
2247                 } else {
2248                     let sline = line.split("///").last().unwrap_or("");
2249                     let line = sline.trim_start();
2250                     if line.starts_with("```") {
2251                         if ignore {
2252                             ignore = false;
2253                             None
2254                         } else {
2255                             ignore = true;
2256                             Some(line_nb + 1)
2257                         }
2258                     } else {
2259                         None
2260                     }
2261                 }
2262             })
2263             .collect()
2264     }
2265
2266     fn check_rustdoc_test_option(&self, res: ProcRes) {
2267         let mut other_files = Vec::new();
2268         let mut files: HashMap<String, Vec<usize>> = HashMap::new();
2269         let cwd = env::current_dir().unwrap();
2270         files.insert(
2271             self.testpaths
2272                 .file
2273                 .strip_prefix(&cwd)
2274                 .unwrap_or(&self.testpaths.file)
2275                 .to_str()
2276                 .unwrap()
2277                 .replace('\\', "/"),
2278             self.get_lines(&self.testpaths.file, Some(&mut other_files)),
2279         );
2280         for other_file in other_files {
2281             let mut path = self.testpaths.file.clone();
2282             path.set_file_name(&format!("{}.rs", other_file));
2283             files.insert(
2284                 path.strip_prefix(&cwd)
2285                     .unwrap_or(&path)
2286                     .to_str()
2287                     .unwrap()
2288                     .replace('\\', "/"),
2289                 self.get_lines(&path, None),
2290             );
2291         }
2292
2293         let mut tested = 0;
2294         for _ in res
2295             .stdout
2296             .split('\n')
2297             .filter(|s| s.starts_with("test "))
2298             .inspect(|s| {
2299                 let tmp: Vec<&str> = s.split(" - ").collect();
2300                 if tmp.len() == 2 {
2301                     let path = tmp[0].rsplit("test ").next().unwrap();
2302                     if let Some(ref mut v) = files.get_mut(&path.replace('\\', "/")) {
2303                         tested += 1;
2304                         let mut iter = tmp[1].split("(line ");
2305                         iter.next();
2306                         let line = iter
2307                             .next()
2308                             .unwrap_or(")")
2309                             .split(')')
2310                             .next()
2311                             .unwrap_or("0")
2312                             .parse()
2313                             .unwrap_or(0);
2314                         if let Ok(pos) = v.binary_search(&line) {
2315                             v.remove(pos);
2316                         } else {
2317                             self.fatal_proc_rec(
2318                                 &format!("Not found doc test: \"{}\" in \"{}\":{:?}", s, path, v),
2319                                 &res,
2320                             );
2321                         }
2322                     }
2323                 }
2324             }) {}
2325         if tested == 0 {
2326             self.fatal_proc_rec(&format!("No test has been found... {:?}", files), &res);
2327         } else {
2328             for (entry, v) in &files {
2329                 if !v.is_empty() {
2330                     self.fatal_proc_rec(
2331                         &format!(
2332                             "Not found test at line{} \"{}\":{:?}",
2333                             if v.len() > 1 { "s" } else { "" },
2334                             entry,
2335                             v
2336                         ),
2337                         &res,
2338                     );
2339                 }
2340             }
2341         }
2342     }
2343
2344     fn run_codegen_units_test(&self) {
2345         assert!(self.revision.is_none(), "revisions not relevant here");
2346
2347         let proc_res = self.compile_test();
2348
2349         if !proc_res.status.success() {
2350             self.fatal_proc_rec("compilation failed!", &proc_res);
2351         }
2352
2353         self.check_no_compiler_crash(&proc_res);
2354
2355         const PREFIX: &'static str = "MONO_ITEM ";
2356         const CGU_MARKER: &'static str = "@@";
2357
2358         let actual: Vec<MonoItem> = proc_res
2359             .stdout
2360             .lines()
2361             .filter(|line| line.starts_with(PREFIX))
2362             .map(|line| str_to_mono_item(line, true))
2363             .collect();
2364
2365         let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None)
2366             .iter()
2367             .map(|e| str_to_mono_item(&e.msg[..], false))
2368             .collect();
2369
2370         let mut missing = Vec::new();
2371         let mut wrong_cgus = Vec::new();
2372
2373         for expected_item in &expected {
2374             let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name);
2375
2376             if let Some(actual_item) = actual_item_with_same_name {
2377                 if !expected_item.codegen_units.is_empty() &&
2378                    // Also check for codegen units
2379                    expected_item.codegen_units != actual_item.codegen_units
2380                 {
2381                     wrong_cgus.push((expected_item.clone(), actual_item.clone()));
2382                 }
2383             } else {
2384                 missing.push(expected_item.string.clone());
2385             }
2386         }
2387
2388         let unexpected: Vec<_> = actual
2389             .iter()
2390             .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
2391             .map(|acgu| acgu.string.clone())
2392             .collect();
2393
2394         if !missing.is_empty() {
2395             missing.sort();
2396
2397             println!("\nThese items should have been contained but were not:\n");
2398
2399             for item in &missing {
2400                 println!("{}", item);
2401             }
2402
2403             println!("\n");
2404         }
2405
2406         if !unexpected.is_empty() {
2407             let sorted = {
2408                 let mut sorted = unexpected.clone();
2409                 sorted.sort();
2410                 sorted
2411             };
2412
2413             println!("\nThese items were contained but should not have been:\n");
2414
2415             for item in sorted {
2416                 println!("{}", item);
2417             }
2418
2419             println!("\n");
2420         }
2421
2422         if !wrong_cgus.is_empty() {
2423             wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
2424             println!("\nThe following items were assigned to wrong codegen units:\n");
2425
2426             for &(ref expected_item, ref actual_item) in &wrong_cgus {
2427                 println!("{}", expected_item.name);
2428                 println!(
2429                     "  expected: {}",
2430                     codegen_units_to_str(&expected_item.codegen_units)
2431                 );
2432                 println!(
2433                     "  actual:   {}",
2434                     codegen_units_to_str(&actual_item.codegen_units)
2435                 );
2436                 println!("");
2437             }
2438         }
2439
2440         if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) {
2441             panic!();
2442         }
2443
2444         #[derive(Clone, Eq, PartialEq)]
2445         struct MonoItem {
2446             name: String,
2447             codegen_units: HashSet<String>,
2448             string: String,
2449         }
2450
2451         // [MONO_ITEM] name [@@ (cgu)+]
2452         fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem {
2453             let s = if s.starts_with(PREFIX) {
2454                 (&s[PREFIX.len()..]).trim()
2455             } else {
2456                 s.trim()
2457             };
2458
2459             let full_string = format!("{}{}", PREFIX, s);
2460
2461             let parts: Vec<&str> = s
2462                 .split(CGU_MARKER)
2463                 .map(str::trim)
2464                 .filter(|s| !s.is_empty())
2465                 .collect();
2466
2467             let name = parts[0].trim();
2468
2469             let cgus = if parts.len() > 1 {
2470                 let cgus_str = parts[1];
2471
2472                 cgus_str
2473                     .split(' ')
2474                     .map(str::trim)
2475                     .filter(|s| !s.is_empty())
2476                     .map(|s| {
2477                         if cgu_has_crate_disambiguator {
2478                             remove_crate_disambiguator_from_cgu(s)
2479                         } else {
2480                             s.to_string()
2481                         }
2482                     })
2483                     .collect()
2484             } else {
2485                 HashSet::new()
2486             };
2487
2488             MonoItem {
2489                 name: name.to_owned(),
2490                 codegen_units: cgus,
2491                 string: full_string,
2492             }
2493         }
2494
2495         fn codegen_units_to_str(cgus: &HashSet<String>) -> String {
2496             let mut cgus: Vec<_> = cgus.iter().collect();
2497             cgus.sort();
2498
2499             let mut string = String::new();
2500             for cgu in cgus {
2501                 string.push_str(&cgu[..]);
2502                 string.push_str(" ");
2503             }
2504
2505             string
2506         }
2507
2508         // Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or
2509         // the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>,
2510         // remove all crate-disambiguators.
2511         fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String {
2512             lazy_static! {
2513                 static ref RE: Regex = Regex::new(
2514                     r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?"
2515                 ).unwrap();
2516             }
2517
2518             let captures = RE.captures(cgu).unwrap_or_else(|| {
2519                 panic!("invalid cgu name encountered: {}", cgu)
2520             });
2521
2522             let mut new_name = cgu.to_owned();
2523
2524             if let Some(d2) = captures.name("d2") {
2525                 new_name.replace_range(d2.start() .. d2.end(), "");
2526             }
2527
2528             let d1 = captures.name("d1").unwrap();
2529             new_name.replace_range(d1.start() .. d1.end(), "");
2530
2531             new_name
2532         }
2533     }
2534
2535     fn init_incremental_test(&self) {
2536         // (See `run_incremental_test` for an overview of how incremental tests work.)
2537
2538         // Before any of the revisions have executed, create the
2539         // incremental workproduct directory.  Delete any old
2540         // incremental work products that may be there from prior
2541         // runs.
2542         let incremental_dir = self.incremental_dir();
2543         if incremental_dir.exists() {
2544             // Canonicalizing the path will convert it to the //?/ format
2545             // on Windows, which enables paths longer than 260 character
2546             let canonicalized = incremental_dir.canonicalize().unwrap();
2547             fs::remove_dir_all(canonicalized).unwrap();
2548         }
2549         fs::create_dir_all(&incremental_dir).unwrap();
2550
2551         if self.config.verbose {
2552             print!(
2553                 "init_incremental_test: incremental_dir={}",
2554                 incremental_dir.display()
2555             );
2556         }
2557     }
2558
2559     fn run_incremental_test(&self) {
2560         // Basic plan for a test incremental/foo/bar.rs:
2561         // - load list of revisions rpass1, cfail2, rpass3
2562         //   - each should begin with `rpass`, `cfail`, or `rfail`
2563         //   - if `rpass`, expect compile and execution to succeed
2564         //   - if `cfail`, expect compilation to fail
2565         //   - if `rfail`, expect execution to fail
2566         // - create a directory build/foo/bar.incremental
2567         // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass1
2568         //   - because name of revision starts with "rpass", expect success
2569         // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C cfail2
2570         //   - because name of revision starts with "cfail", expect an error
2571         //   - load expected errors as usual, but filter for those that end in `[rfail2]`
2572         // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass3
2573         //   - because name of revision starts with "rpass", expect success
2574         // - execute build/foo/bar.exe and save output
2575         //
2576         // FIXME -- use non-incremental mode as an oracle? That doesn't apply
2577         // to #[rustc_dirty] and clean tests I guess
2578
2579         let revision = self
2580             .revision
2581             .expect("incremental tests require a list of revisions");
2582
2583         // Incremental workproduct directory should have already been created.
2584         let incremental_dir = self.incremental_dir();
2585         assert!(
2586             incremental_dir.exists(),
2587             "init_incremental_test failed to create incremental dir"
2588         );
2589
2590         // Add an extra flag pointing at the incremental directory.
2591         let mut revision_props = self.props.clone();
2592         revision_props.incremental_dir = Some(incremental_dir);
2593
2594         let revision_cx = TestCx {
2595             config: self.config,
2596             props: &revision_props,
2597             testpaths: self.testpaths,
2598             revision: self.revision,
2599         };
2600
2601         if self.config.verbose {
2602             print!(
2603                 "revision={:?} revision_props={:#?}",
2604                 revision, revision_props
2605             );
2606         }
2607
2608         if revision.starts_with("rpass") {
2609             revision_cx.run_rpass_test();
2610         } else if revision.starts_with("rfail") {
2611             revision_cx.run_rfail_test();
2612         } else if revision.starts_with("cfail") {
2613             revision_cx.run_cfail_test();
2614         } else {
2615             revision_cx.fatal("revision name must begin with rpass, rfail, or cfail");
2616         }
2617     }
2618
2619     /// Directory where incremental work products are stored.
2620     fn incremental_dir(&self) -> PathBuf {
2621         self.output_base_name().with_extension("inc")
2622     }
2623
2624     fn run_rmake_test(&self) {
2625         let cwd = env::current_dir().unwrap();
2626         let src_root = self
2627             .config
2628             .src_base
2629             .parent()
2630             .unwrap()
2631             .parent()
2632             .unwrap()
2633             .parent()
2634             .unwrap();
2635         let src_root = cwd.join(&src_root);
2636
2637         let tmpdir = cwd.join(self.output_base_name());
2638         if tmpdir.exists() {
2639             self.aggressive_rm_rf(&tmpdir).unwrap();
2640         }
2641         create_dir_all(&tmpdir).unwrap();
2642
2643         let host = &self.config.host;
2644         let make = if host.contains("bitrig")
2645             || host.contains("dragonfly")
2646             || host.contains("freebsd")
2647             || host.contains("netbsd")
2648             || host.contains("openbsd")
2649         {
2650             "gmake"
2651         } else {
2652             "make"
2653         };
2654
2655         let mut cmd = Command::new(make);
2656         cmd.current_dir(&self.testpaths.file)
2657             .stdout(Stdio::piped())
2658             .stderr(Stdio::piped())
2659             .env("TARGET", &self.config.target)
2660             .env("PYTHON", &self.config.docck_python)
2661             .env("S", src_root)
2662             .env("RUST_BUILD_STAGE", &self.config.stage_id)
2663             .env("RUSTC", cwd.join(&self.config.rustc_path))
2664             .env("TMPDIR", &tmpdir)
2665             .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
2666             .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
2667             .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
2668             .env("LLVM_COMPONENTS", &self.config.llvm_components)
2669             .env("LLVM_CXXFLAGS", &self.config.llvm_cxxflags)
2670
2671             // We for sure don't want these tests to run in parallel, so make
2672             // sure they don't have access to these vars if we run via `make`
2673             // at the top level
2674             .env_remove("MAKEFLAGS")
2675             .env_remove("MFLAGS")
2676             .env_remove("CARGO_MAKEFLAGS");
2677
2678         if let Some(ref rustdoc) = self.config.rustdoc_path {
2679             cmd.env("RUSTDOC", cwd.join(rustdoc));
2680         }
2681
2682         if let Some(ref node) = self.config.nodejs {
2683             cmd.env("NODE", node);
2684         }
2685
2686         if let Some(ref linker) = self.config.linker {
2687             cmd.env("RUSTC_LINKER", linker);
2688         }
2689
2690         if let Some(ref clang) = self.config.run_clang_based_tests_with {
2691             cmd.env("CLANG", clang);
2692         }
2693
2694         if let Some(ref filecheck) = self.config.llvm_filecheck {
2695             cmd.env("LLVM_FILECHECK", filecheck);
2696         }
2697
2698         if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir {
2699             cmd.env("LLVM_BIN_DIR", llvm_bin_dir);
2700         }
2701
2702         // We don't want RUSTFLAGS set from the outside to interfere with
2703         // compiler flags set in the test cases:
2704         cmd.env_remove("RUSTFLAGS");
2705
2706         if self.config.target.contains("msvc") && self.config.cc != "" {
2707             // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
2708             // and that `lib.exe` lives next to it.
2709             let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
2710
2711             // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
2712             // a path and instead passes `C:\msys64\foo`, so convert all
2713             // `/`-arguments to MSVC here to `-` arguments.
2714             let cflags = self
2715                 .config
2716                 .cflags
2717                 .split(' ')
2718                 .map(|s| s.replace("/", "-"))
2719                 .collect::<Vec<_>>()
2720                 .join(" ");
2721
2722             cmd.env("IS_MSVC", "1")
2723                 .env("IS_WINDOWS", "1")
2724                 .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
2725                 .env("CC", format!("'{}' {}", self.config.cc, cflags))
2726                 .env("CXX", format!("'{}'", &self.config.cxx));
2727         } else {
2728             cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
2729                 .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags))
2730                 .env("AR", &self.config.ar);
2731
2732             if self.config.target.contains("windows") {
2733                 cmd.env("IS_WINDOWS", "1");
2734             }
2735         }
2736
2737         let output = cmd
2738             .spawn()
2739             .and_then(read2_abbreviated)
2740             .expect("failed to spawn `make`");
2741         if !output.status.success() {
2742             let res = ProcRes {
2743                 status: output.status,
2744                 stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
2745                 stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
2746                 cmdline: format!("{:?}", cmd),
2747             };
2748             self.fatal_proc_rec("make failed", &res);
2749         }
2750     }
2751
2752     fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
2753         for e in path.read_dir()? {
2754             let entry = e?;
2755             let path = entry.path();
2756             if entry.file_type()?.is_dir() {
2757                 self.aggressive_rm_rf(&path)?;
2758             } else {
2759                 // Remove readonly files as well on windows (by default we can't)
2760                 fs::remove_file(&path).or_else(|e| {
2761                     if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied {
2762                         let mut meta = entry.metadata()?.permissions();
2763                         meta.set_readonly(false);
2764                         fs::set_permissions(&path, meta)?;
2765                         fs::remove_file(&path)
2766                     } else {
2767                         Err(e)
2768                     }
2769                 })?;
2770             }
2771         }
2772         fs::remove_dir(path)
2773     }
2774
2775     fn run_js_doc_test(&self) {
2776         if let Some(nodejs) = &self.config.nodejs {
2777             let out_dir = self.output_base_dir();
2778
2779             self.document(&out_dir);
2780
2781             let root = self.config.find_rust_src_root().unwrap();
2782             let res = self.cmd2procres(
2783                 Command::new(&nodejs)
2784                     .arg(root.join("src/tools/rustdoc-js/tester.js"))
2785                     .arg(out_dir.parent().expect("no parent"))
2786                     .arg(&self.testpaths.file.file_stem().expect("couldn't get file stem")),
2787             );
2788             if !res.status.success() {
2789                 self.fatal_proc_rec("rustdoc-js test failed!", &res);
2790             }
2791         } else {
2792             self.fatal("no nodeJS");
2793         }
2794     }
2795
2796     fn run_ui_test(&self) {
2797         // if the user specified a format in the ui test
2798         // print the output to the stderr file, otherwise extract
2799         // the rendered error messages from json and print them
2800         let explicit = self
2801             .props
2802             .compile_flags
2803             .iter()
2804             .any(|s| s.contains("--error-format"));
2805         let proc_res = self.compile_test();
2806         self.check_if_test_should_compile(&proc_res);
2807
2808         let expected_stderr = self.load_expected_output(UI_STDERR);
2809         let expected_stdout = self.load_expected_output(UI_STDOUT);
2810         let expected_fixed = self.load_expected_output(UI_FIXED);
2811
2812         let normalized_stdout =
2813             self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout);
2814
2815         let stderr = if explicit {
2816             proc_res.stderr.clone()
2817         } else {
2818             json::extract_rendered(&proc_res.stderr)
2819         };
2820
2821         let normalized_stderr = self.normalize_output(&stderr, &self.props.normalize_stderr);
2822
2823         let mut errors = 0;
2824         if !self.props.dont_check_compiler_stdout {
2825             errors += self.compare_output("stdout", &normalized_stdout, &expected_stdout);
2826         }
2827         if !self.props.dont_check_compiler_stderr {
2828             errors += self.compare_output("stderr", &normalized_stderr, &expected_stderr);
2829         }
2830
2831         let modes_to_prune = vec![CompareMode::Nll];
2832         self.prune_duplicate_outputs(&modes_to_prune);
2833
2834         if self.config.compare_mode.is_some() {
2835             // don't test rustfix with nll right now
2836         } else if self.config.rustfix_coverage {
2837             // Find out which tests have `MachineApplicable` suggestions but are missing
2838             // `run-rustfix` or `run-rustfix-only-machine-applicable` headers.
2839             //
2840             // This will return an empty `Vec` in case the executed test file has a
2841             // `compile-flags: --error-format=xxxx` header with a value other than `json`.
2842             let suggestions = get_suggestions_from_json(
2843                 &proc_res.stderr,
2844                 &HashSet::new(),
2845                 Filter::MachineApplicableOnly
2846             ).unwrap_or_default();
2847             if suggestions.len() > 0
2848                 && !self.props.run_rustfix
2849                 && !self.props.rustfix_only_machine_applicable {
2850                     let mut coverage_file_path = self.config.build_base.clone();
2851                     coverage_file_path.push("rustfix_missing_coverage.txt");
2852                     debug!("coverage_file_path: {}", coverage_file_path.display());
2853
2854                     let mut file = OpenOptions::new()
2855                         .create(true)
2856                         .append(true)
2857                         .open(coverage_file_path.as_path())
2858                         .expect("could not create or open file");
2859
2860                     if let Err(_) = writeln!(file, "{}", self.testpaths.file.display()) {
2861                         panic!("couldn't write to {}", coverage_file_path.display());
2862                     }
2863             }
2864         } else if self.props.run_rustfix {
2865             // Apply suggestions from rustc to the code itself
2866             let unfixed_code = self
2867                 .load_expected_output_from_path(&self.testpaths.file)
2868                 .unwrap();
2869             let suggestions = get_suggestions_from_json(
2870                 &proc_res.stderr,
2871                 &HashSet::new(),
2872                 if self.props.rustfix_only_machine_applicable {
2873                     Filter::MachineApplicableOnly
2874                 } else {
2875                     Filter::Everything
2876                 },
2877             ).unwrap();
2878             let fixed_code = apply_suggestions(&unfixed_code, &suggestions).expect(&format!(
2879                 "failed to apply suggestions for {:?} with rustfix",
2880                 self.testpaths.file
2881             ));
2882
2883             errors += self.compare_output("fixed", &fixed_code, &expected_fixed);
2884         } else if !expected_fixed.is_empty() {
2885             panic!(
2886                 "the `// run-rustfix` directive wasn't found but a `*.fixed` \
2887                  file was found"
2888             );
2889         }
2890
2891         if errors > 0 {
2892             println!("To update references, rerun the tests and pass the `--bless` flag");
2893             let relative_path_to_file = self
2894                 .testpaths
2895                 .relative_dir
2896                 .join(self.testpaths.file.file_name().unwrap());
2897             println!(
2898                 "To only update this specific test, also pass `--test-args {}`",
2899                 relative_path_to_file.display(),
2900             );
2901             self.fatal_proc_rec(
2902                 &format!("{} errors occurred comparing output.", errors),
2903                 &proc_res,
2904             );
2905         }
2906
2907         let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
2908
2909         if self.should_run_successfully() {
2910             let proc_res = self.exec_compiled_test();
2911
2912             if !proc_res.status.success() {
2913                 self.fatal_proc_rec("test run failed!", &proc_res);
2914             }
2915         }
2916
2917         debug!("run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \
2918                proc_res.status={:?} props.error_patterns={:?}",
2919                explicit, self.config.compare_mode, expected_errors, proc_res.status,
2920                self.props.error_patterns);
2921         if !explicit && self.config.compare_mode.is_none() {
2922             if !proc_res.status.success() {
2923                 if !self.props.error_patterns.is_empty() {
2924                     // "// error-pattern" comments
2925                     self.check_error_patterns(&proc_res.stderr, &proc_res);
2926                 } else {
2927                     // "//~ERROR comments"
2928                     self.check_expected_errors(expected_errors, &proc_res);
2929                 }
2930             }
2931         }
2932
2933         if self.props.run_rustfix && self.config.compare_mode.is_none() {
2934             // And finally, compile the fixed code and make sure it both
2935             // succeeds and has no diagnostics.
2936             let mut rustc = self.make_compile_args(
2937                 &self.testpaths.file.with_extension(UI_FIXED),
2938                 TargetLocation::ThisFile(self.make_exe_name()),
2939             );
2940             rustc.arg("-L").arg(&self.aux_output_dir_name());
2941             let res = self.compose_and_run_compiler(rustc, None);
2942             if !res.status.success() {
2943                 self.fatal_proc_rec("failed to compile fixed code", &res);
2944             }
2945             if !res.stderr.is_empty() && !self.props.rustfix_only_machine_applicable {
2946                 self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
2947             }
2948         }
2949     }
2950
2951     fn run_mir_opt_test(&self) {
2952         let proc_res = self.compile_test();
2953
2954         if !proc_res.status.success() {
2955             self.fatal_proc_rec("compilation failed!", &proc_res);
2956         }
2957
2958         let proc_res = self.exec_compiled_test();
2959
2960         if !proc_res.status.success() {
2961             self.fatal_proc_rec("test run failed!", &proc_res);
2962         }
2963         self.check_mir_dump();
2964     }
2965
2966     fn check_mir_dump(&self) {
2967         let test_file_contents = fs::read_to_string(&self.testpaths.file).unwrap();
2968         if let Some(idx) = test_file_contents.find("// END RUST SOURCE") {
2969             let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len());
2970             let tests_text_str = String::from(tests_text);
2971             let mut curr_test: Option<&str> = None;
2972             let mut curr_test_contents = vec![ExpectedLine::Elision];
2973             for l in tests_text_str.lines() {
2974                 debug!("line: {:?}", l);
2975                 if l.starts_with("// START ") {
2976                     let (_, t) = l.split_at("// START ".len());
2977                     curr_test = Some(t);
2978                 } else if l.starts_with("// END") {
2979                     let (_, t) = l.split_at("// END ".len());
2980                     if Some(t) != curr_test {
2981                         panic!("mismatched START END test name");
2982                     }
2983                     self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents);
2984                     curr_test = None;
2985                     curr_test_contents.clear();
2986                     curr_test_contents.push(ExpectedLine::Elision);
2987                 } else if l.is_empty() {
2988                     // ignore
2989                 } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." {
2990                     curr_test_contents.push(ExpectedLine::Elision)
2991                 } else if l.starts_with("// ") {
2992                     let (_, test_content) = l.split_at("// ".len());
2993                     curr_test_contents.push(ExpectedLine::Text(test_content));
2994                 }
2995             }
2996         }
2997     }
2998
2999     fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) {
3000         let t = |file| FileTime::from_last_modification_time(&fs::metadata(file).unwrap());
3001         let source_file = &self.testpaths.file;
3002         let output_time = t(output_file);
3003         let source_time = t(source_file);
3004         if source_time > output_time {
3005             debug!(
3006                 "source file time: {:?} output file time: {:?}",
3007                 source_time, output_time
3008             );
3009             panic!(
3010                 "test source file `{}` is newer than potentially stale output file `{}`.",
3011                 source_file.display(),
3012                 test_name
3013             );
3014         }
3015     }
3016
3017     fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&str>]) {
3018         let mut output_file = PathBuf::new();
3019         output_file.push(self.get_mir_dump_dir());
3020         output_file.push(test_name);
3021         debug!("comparing the contests of: {:?}", output_file);
3022         debug!("with: {:?}", expected_content);
3023         if !output_file.exists() {
3024             panic!(
3025                 "Output file `{}` from test does not exist",
3026                 output_file.into_os_string().to_string_lossy()
3027             );
3028         }
3029         self.check_mir_test_timestamp(test_name, &output_file);
3030
3031         let dumped_string = fs::read_to_string(&output_file).unwrap();
3032         let mut dumped_lines = dumped_string
3033             .lines()
3034             .map(|l| nocomment_mir_line(l))
3035             .filter(|l| !l.is_empty());
3036         let mut expected_lines = expected_content
3037             .iter()
3038             .filter(|&l| {
3039                 if let &ExpectedLine::Text(l) = l {
3040                     !l.is_empty()
3041                 } else {
3042                     true
3043                 }
3044             })
3045             .peekable();
3046
3047         let compare = |expected_line, dumped_line| {
3048             let e_norm = normalize_mir_line(expected_line);
3049             let d_norm = normalize_mir_line(dumped_line);
3050             debug!("found: {:?}", d_norm);
3051             debug!("expected: {:?}", e_norm);
3052             e_norm == d_norm
3053         };
3054
3055         let error = |expected_line, extra_msg| {
3056             let normalize_all = dumped_string
3057                 .lines()
3058                 .map(nocomment_mir_line)
3059                 .filter(|l| !l.is_empty())
3060                 .collect::<Vec<_>>()
3061                 .join("\n");
3062             let f = |l: &ExpectedLine<_>| match l {
3063                 &ExpectedLine::Elision => "... (elided)".into(),
3064                 &ExpectedLine::Text(t) => t,
3065             };
3066             let expected_content = expected_content
3067                 .iter()
3068                 .map(|l| f(l))
3069                 .collect::<Vec<_>>()
3070                 .join("\n");
3071             panic!(
3072                 "Did not find expected line, error: {}\n\
3073                  Expected Line: {:?}\n\
3074                  Test Name: {}\n\
3075                  Expected:\n{}\n\
3076                  Actual:\n{}",
3077                 extra_msg, expected_line, test_name, expected_content, normalize_all
3078             );
3079         };
3080
3081         // We expect each non-empty line to appear consecutively, non-consecutive lines
3082         // must be separated by at least one Elision
3083         let mut start_block_line = None;
3084         while let Some(dumped_line) = dumped_lines.next() {
3085             match expected_lines.next() {
3086                 Some(&ExpectedLine::Text(expected_line)) => {
3087                     let normalized_expected_line = normalize_mir_line(expected_line);
3088                     if normalized_expected_line.contains(":{") {
3089                         start_block_line = Some(expected_line);
3090                     }
3091
3092                     if !compare(expected_line, dumped_line) {
3093                         error!("{:?}", start_block_line);
3094                         error(
3095                             expected_line,
3096                             format!(
3097                                 "Mismatch in lines\n\
3098                                  Current block: {}\n\
3099                                  Actual Line: {:?}",
3100                                 start_block_line.unwrap_or("None"),
3101                                 dumped_line
3102                             ),
3103                         );
3104                     }
3105                 }
3106                 Some(&ExpectedLine::Elision) => {
3107                     // skip any number of elisions in a row.
3108                     while let Some(&&ExpectedLine::Elision) = expected_lines.peek() {
3109                         expected_lines.next();
3110                     }
3111                     if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() {
3112                         let mut found = compare(expected_line, dumped_line);
3113                         if found {
3114                             continue;
3115                         }
3116                         while let Some(dumped_line) = dumped_lines.next() {
3117                             found = compare(expected_line, dumped_line);
3118                             if found {
3119                                 break;
3120                             }
3121                         }
3122                         if !found {
3123                             error(expected_line, "ran out of mir dump to match against".into());
3124                         }
3125                     }
3126                 }
3127                 None => {}
3128             }
3129         }
3130     }
3131
3132     fn get_mir_dump_dir(&self) -> PathBuf {
3133         let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
3134         debug!("input_file: {:?}", self.testpaths.file);
3135         mir_dump_dir.push(&self.testpaths.relative_dir);
3136         mir_dump_dir.push(self.testpaths.file.file_stem().unwrap());
3137         mir_dump_dir
3138     }
3139
3140     fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
3141         let parent_dir = self.testpaths.file.parent().unwrap();
3142         let cflags = self.props.compile_flags.join(" ");
3143         let json = cflags.contains("--error-format json")
3144             || cflags.contains("--error-format pretty-json")
3145             || cflags.contains("--error-format=json")
3146             || cflags.contains("--error-format=pretty-json");
3147         let parent_dir_str = if json {
3148             parent_dir.display().to_string().replace("\\", "\\\\")
3149         } else {
3150             parent_dir.display().to_string()
3151         };
3152
3153         let mut normalized = output.replace(&parent_dir_str, "$DIR");
3154
3155         // Paths into the libstd/libcore
3156         let src_dir = self.config.src_base.parent().unwrap().parent().unwrap();
3157         let src_dir_str = if json {
3158             src_dir.display().to_string().replace("\\", "\\\\")
3159         } else {
3160             src_dir.display().to_string()
3161         };
3162         normalized = normalized.replace(&src_dir_str, "$SRC_DIR");
3163
3164         // Paths into the build directory
3165         let test_build_dir = &self.config.build_base;
3166         let parent_build_dir = test_build_dir.parent().unwrap().parent().unwrap().parent().unwrap();
3167
3168         // eg. /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui
3169         normalized = normalized.replace(test_build_dir.to_str().unwrap(), "$TEST_BUILD_DIR");
3170         // eg. /home/user/rust/build
3171         normalized = normalized.replace(&parent_build_dir.to_str().unwrap(), "$BUILD_DIR");
3172
3173         // Paths into lib directory.
3174         let mut lib_dir = parent_build_dir.parent().unwrap().to_path_buf();
3175         lib_dir.push("lib");
3176         normalized = normalized.replace(&lib_dir.to_str().unwrap(), "$LIB_DIR");
3177
3178         if json {
3179             // escaped newlines in json strings should be readable
3180             // in the stderr files. There's no point int being correct,
3181             // since only humans process the stderr files.
3182             // Thus we just turn escaped newlines back into newlines.
3183             normalized = normalized.replace("\\n", "\n");
3184         }
3185
3186         // If there are `$SRC_DIR` normalizations with line and column numbers, then replace them
3187         // with placeholders as we do not want tests needing updated when compiler source code
3188         // changes.
3189         // eg. $SRC_DIR/libcore/mem.rs:323:14 becomes $SRC_DIR/libcore/mem.rs:LL:COL
3190         normalized = Regex::new("SRC_DIR(.+):\\d+:\\d+").unwrap()
3191             .replace_all(&normalized, "SRC_DIR$1:LL:COL").into_owned();
3192
3193         normalized = Self::normalize_platform_differences(&normalized);
3194         normalized = normalized.replace("\t", "\\t"); // makes tabs visible
3195
3196         // Remove test annotations like `//~ ERROR text` from the output,
3197         // since they duplicate actual errors and make the output hard to read.
3198         normalized = Regex::new("\\s*//(\\[.*\\])?~.*").unwrap()
3199             .replace_all(&normalized, "").into_owned();
3200
3201         for rule in custom_rules {
3202             let re = Regex::new(&rule.0).expect("bad regex in custom normalization rule");
3203             normalized = re.replace_all(&normalized, &rule.1[..]).into_owned();
3204         }
3205         normalized
3206     }
3207
3208     /// Normalize output differences across platforms. Generally changes Windows output to be more
3209     /// Unix-like.
3210     ///
3211     /// Replaces backslashes in paths with forward slashes, and replaces CRLF line endings
3212     /// with LF.
3213     fn normalize_platform_differences(output: &str) -> String {
3214         lazy_static! {
3215             /// Used to find Windows paths.
3216             ///
3217             /// It's not possible to detect paths in the error messages generally, but this is a
3218             /// decent enough heuristic.
3219             static ref PATH_BACKSLASH_RE: Regex = Regex::new(r#"(?x)
3220                 (?:
3221                   # Match paths that don't include spaces.
3222                   (?:\\[\pL\pN\.\-_']+)+\.\pL+
3223                 |
3224                   # If the path starts with a well-known root, then allow spaces.
3225                   \$(?:DIR|SRC_DIR|TEST_BUILD_DIR|BUILD_DIR|LIB_DIR)(?:\\[\pL\pN\.\-_' ]+)+
3226                 )"#
3227             ).unwrap();
3228         }
3229
3230         let output = output.replace(r"\\", r"\");
3231
3232         PATH_BACKSLASH_RE.replace_all(&output, |caps: &Captures<'_>| {
3233             println!("{}", &caps[0]);
3234             caps[0].replace(r"\", "/")
3235         }).replace("\r\n", "\n")
3236     }
3237
3238     fn expected_output_path(&self, kind: &str) -> PathBuf {
3239         let mut path = expected_output_path(
3240             &self.testpaths,
3241             self.revision,
3242             &self.config.compare_mode,
3243             kind,
3244         );
3245
3246         if !path.exists() {
3247             if let Some(CompareMode::Polonius) = self.config.compare_mode {
3248                 path = expected_output_path(
3249                     &self.testpaths,
3250                     self.revision,
3251                     &Some(CompareMode::Nll),
3252                     kind,
3253                 );
3254             }
3255         }
3256
3257         if !path.exists() {
3258             path = expected_output_path(&self.testpaths, self.revision, &None, kind);
3259         }
3260
3261         path
3262     }
3263
3264     fn load_expected_output(&self, kind: &str) -> String {
3265         let path = self.expected_output_path(kind);
3266         if path.exists() {
3267             match self.load_expected_output_from_path(&path) {
3268                 Ok(x) => x,
3269                 Err(x) => self.fatal(&x),
3270             }
3271         } else {
3272             String::new()
3273         }
3274     }
3275
3276     fn load_expected_output_from_path(&self, path: &Path) -> Result<String, String> {
3277         fs::read_to_string(path).map_err(|err| {
3278             format!("failed to load expected output from `{}`: {}", path.display(), err)
3279         })
3280     }
3281
3282     fn delete_file(&self, file: &PathBuf) {
3283         if let Err(e) = fs::remove_file(file) {
3284             self.fatal(&format!(
3285                 "failed to delete `{}`: {}",
3286                 file.display(),
3287                 e,
3288             ));
3289         }
3290     }
3291
3292     fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
3293         if actual == expected {
3294             return 0;
3295         }
3296
3297         if !self.config.bless {
3298             if expected.is_empty() {
3299                 println!("normalized {}:\n{}\n", kind, actual);
3300             } else {
3301                 println!("diff of {}:\n", kind);
3302                 let diff_results = make_diff(expected, actual, 3);
3303                 for result in diff_results {
3304                     let mut line_number = result.line_number;
3305                     for line in result.lines {
3306                         match line {
3307                             DiffLine::Expected(e) => {
3308                                 println!("-\t{}", e);
3309                                 line_number += 1;
3310                             }
3311                             DiffLine::Context(c) => {
3312                                 println!("{}\t{}", line_number, c);
3313                                 line_number += 1;
3314                             }
3315                             DiffLine::Resulting(r) => {
3316                                 println!("+\t{}", r);
3317                             }
3318                         }
3319                     }
3320                     println!("");
3321                 }
3322             }
3323         }
3324
3325         let mode = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
3326         let output_file = self
3327             .output_base_name()
3328             .with_extra_extension(self.revision.unwrap_or(""))
3329             .with_extra_extension(mode)
3330             .with_extra_extension(kind);
3331
3332         let mut files = vec![output_file];
3333         if self.config.bless {
3334             files.push(expected_output_path(
3335                 self.testpaths,
3336                 self.revision,
3337                 &self.config.compare_mode,
3338                 kind,
3339             ));
3340         }
3341
3342         for output_file in &files {
3343             if actual.is_empty() {
3344                 self.delete_file(output_file);
3345             } else if let Err(err) = fs::write(&output_file, &actual) {
3346                 self.fatal(&format!(
3347                     "failed to write {} to `{}`: {}",
3348                     kind,
3349                     output_file.display(),
3350                     err,
3351                 ));
3352             }
3353         }
3354
3355         println!("\nThe actual {0} differed from the expected {0}.", kind);
3356         for output_file in files {
3357             println!("Actual {} saved to {}", kind, output_file.display());
3358         }
3359         if self.config.bless {
3360             0
3361         } else {
3362             1
3363         }
3364     }
3365
3366     fn prune_duplicate_output(&self, mode: CompareMode, kind: &str, canon_content: &str) {
3367         let examined_path = expected_output_path(
3368             &self.testpaths,
3369             self.revision,
3370             &Some(mode),
3371             kind,
3372         );
3373
3374         let examined_content = self
3375             .load_expected_output_from_path(&examined_path)
3376             .unwrap_or_else(|_| String::new());
3377
3378         if examined_path.exists() && canon_content == &examined_content {
3379             self.delete_file(&examined_path);
3380         }
3381     }
3382
3383     fn prune_duplicate_outputs(&self, modes: &[CompareMode]) {
3384         if self.config.bless {
3385             for kind in UI_EXTENSIONS {
3386                 let canon_comparison_path = expected_output_path(
3387                     &self.testpaths,
3388                     self.revision,
3389                     &None,
3390                     kind,
3391                 );
3392
3393                 if let Ok(canon) = self.load_expected_output_from_path(&canon_comparison_path) {
3394                     for mode in modes {
3395                         self.prune_duplicate_output(mode.clone(), kind, &canon);
3396                     }
3397                 }
3398             }
3399         }
3400     }
3401
3402     fn create_stamp(&self) {
3403         let stamp = crate::stamp(&self.config, self.testpaths, self.revision);
3404         fs::write(&stamp, compute_stamp_hash(&self.config)).unwrap();
3405     }
3406 }
3407
3408 struct ProcArgs {
3409     prog: String,
3410     args: Vec<String>,
3411 }
3412
3413 pub struct ProcRes {
3414     status: ExitStatus,
3415     stdout: String,
3416     stderr: String,
3417     cmdline: String,
3418 }
3419
3420 impl ProcRes {
3421     pub fn fatal(&self, err: Option<&str>) -> ! {
3422         if let Some(e) = err {
3423             println!("\nerror: {}", e);
3424         }
3425         print!(
3426             "\
3427              status: {}\n\
3428              command: {}\n\
3429              stdout:\n\
3430              ------------------------------------------\n\
3431              {}\n\
3432              ------------------------------------------\n\
3433              stderr:\n\
3434              ------------------------------------------\n\
3435              {}\n\
3436              ------------------------------------------\n\
3437              \n",
3438             self.status, self.cmdline,
3439             json::extract_rendered(&self.stdout),
3440             json::extract_rendered(&self.stderr),
3441         );
3442         // Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
3443         // compiletest, which is unnecessary noise.
3444         std::panic::resume_unwind(Box::new(()));
3445     }
3446 }
3447
3448 enum TargetLocation {
3449     ThisFile(PathBuf),
3450     ThisDirectory(PathBuf),
3451 }
3452
3453 #[derive(Clone, PartialEq, Eq)]
3454 enum ExpectedLine<T: AsRef<str>> {
3455     Elision,
3456     Text(T),
3457 }
3458
3459 impl<T> fmt::Debug for ExpectedLine<T>
3460 where
3461     T: AsRef<str> + fmt::Debug,
3462 {
3463     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
3464         if let &ExpectedLine::Text(ref t) = self {
3465             write!(formatter, "{:?}", t)
3466         } else {
3467             write!(formatter, "\"...\" (Elision)")
3468         }
3469     }
3470 }
3471
3472 fn normalize_mir_line(line: &str) -> String {
3473     nocomment_mir_line(line).replace(char::is_whitespace, "")
3474 }
3475
3476 fn nocomment_mir_line(line: &str) -> &str {
3477     if let Some(idx) = line.find("//") {
3478         let (l, _) = line.split_at(idx);
3479         l.trim_end()
3480     } else {
3481         line
3482     }
3483 }
3484
3485 fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
3486     use crate::read2::read2;
3487     use std::mem::replace;
3488
3489     const HEAD_LEN: usize = 160 * 1024;
3490     const TAIL_LEN: usize = 256 * 1024;
3491
3492     enum ProcOutput {
3493         Full(Vec<u8>),
3494         Abbreviated {
3495             head: Vec<u8>,
3496             skipped: usize,
3497             tail: Box<[u8]>,
3498         },
3499     }
3500
3501     impl ProcOutput {
3502         fn extend(&mut self, data: &[u8]) {
3503             let new_self = match *self {
3504                 ProcOutput::Full(ref mut bytes) => {
3505                     bytes.extend_from_slice(data);
3506                     let new_len = bytes.len();
3507                     if new_len <= HEAD_LEN + TAIL_LEN {
3508                         return;
3509                     }
3510                     let tail = bytes.split_off(new_len - TAIL_LEN).into_boxed_slice();
3511                     let head = replace(bytes, Vec::new());
3512                     let skipped = new_len - HEAD_LEN - TAIL_LEN;
3513                     ProcOutput::Abbreviated {
3514                         head,
3515                         skipped,
3516                         tail,
3517                     }
3518                 }
3519                 ProcOutput::Abbreviated {
3520                     ref mut skipped,
3521                     ref mut tail,
3522                     ..
3523                 } => {
3524                     *skipped += data.len();
3525                     if data.len() <= TAIL_LEN {
3526                         tail[..data.len()].copy_from_slice(data);
3527                         tail.rotate_left(data.len());
3528                     } else {
3529                         tail.copy_from_slice(&data[(data.len() - TAIL_LEN)..]);
3530                     }
3531                     return;
3532                 }
3533             };
3534             *self = new_self;
3535         }
3536
3537         fn into_bytes(self) -> Vec<u8> {
3538             match self {
3539                 ProcOutput::Full(bytes) => bytes,
3540                 ProcOutput::Abbreviated {
3541                     mut head,
3542                     skipped,
3543                     tail,
3544                 } => {
3545                     write!(&mut head, "\n\n<<<<<< SKIPPED {} BYTES >>>>>>\n\n", skipped).unwrap();
3546                     head.extend_from_slice(&tail);
3547                     head
3548                 }
3549             }
3550         }
3551     }
3552
3553     let mut stdout = ProcOutput::Full(Vec::new());
3554     let mut stderr = ProcOutput::Full(Vec::new());
3555
3556     drop(child.stdin.take());
3557     read2(
3558         child.stdout.take().unwrap(),
3559         child.stderr.take().unwrap(),
3560         &mut |is_stdout, data, _| {
3561             if is_stdout { &mut stdout } else { &mut stderr }.extend(data);
3562             data.clear();
3563         },
3564     )?;
3565     let status = child.wait()?;
3566
3567     Ok(Output {
3568         status,
3569         stdout: stdout.into_bytes(),
3570         stderr: stderr.into_bytes(),
3571     })
3572 }
3573
3574 #[cfg(test)]
3575 mod tests {
3576     use super::TestCx;
3577
3578     #[test]
3579     fn normalize_platform_differences() {
3580         assert_eq!(
3581             TestCx::normalize_platform_differences(r"$DIR\foo.rs"),
3582             "$DIR/foo.rs"
3583         );
3584         assert_eq!(
3585             TestCx::normalize_platform_differences(r"$BUILD_DIR\..\parser.rs"),
3586             "$BUILD_DIR/../parser.rs"
3587         );
3588         assert_eq!(
3589             TestCx::normalize_platform_differences(r"$DIR\bar.rs hello\nworld"),
3590             r"$DIR/bar.rs hello\nworld"
3591         );
3592         assert_eq!(
3593             TestCx::normalize_platform_differences(r"either bar\baz.rs or bar\baz\mod.rs"),
3594             r"either bar/baz.rs or bar/baz/mod.rs",
3595         );
3596         assert_eq!(
3597             TestCx::normalize_platform_differences(r"`.\some\path.rs`"),
3598             r"`./some/path.rs`",
3599         );
3600         assert_eq!(
3601             TestCx::normalize_platform_differences(r"`some\path.rs`"),
3602             r"`some/path.rs`",
3603         );
3604         assert_eq!(
3605             TestCx::normalize_platform_differences(r"$DIR\path-with-dashes.rs"),
3606             r"$DIR/path-with-dashes.rs"
3607         );
3608         assert_eq!(
3609             TestCx::normalize_platform_differences(r"$DIR\path_with_underscores.rs"),
3610             r"$DIR/path_with_underscores.rs",
3611         );
3612         assert_eq!(
3613             TestCx::normalize_platform_differences(r"$DIR\foo.rs:12:11"), "$DIR/foo.rs:12:11",
3614         );
3615         assert_eq!(
3616             TestCx::normalize_platform_differences(r"$DIR\path with spaces 'n' quotes"),
3617             "$DIR/path with spaces 'n' quotes",
3618         );
3619         assert_eq!(
3620             TestCx::normalize_platform_differences(r"$DIR\file_with\no_extension"),
3621             "$DIR/file_with/no_extension",
3622         );
3623
3624         assert_eq!(TestCx::normalize_platform_differences(r"\n"), r"\n");
3625         assert_eq!(TestCx::normalize_platform_differences(r"{ \n"), r"{ \n");
3626         assert_eq!(TestCx::normalize_platform_differences(r"`\]`"), r"`\]`");
3627         assert_eq!(TestCx::normalize_platform_differences(r#""\{""#), r#""\{""#);
3628         assert_eq!(
3629             TestCx::normalize_platform_differences(r#"write!(&mut v, "Hello\n")"#),
3630             r#"write!(&mut v, "Hello\n")"#
3631         );
3632         assert_eq!(
3633             TestCx::normalize_platform_differences(r#"println!("test\ntest")"#),
3634             r#"println!("test\ntest")"#,
3635         );
3636     }
3637 }