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