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