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