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