Skip to main content

compiletest/
runtest.rs

1use std::borrow::Cow;
2use std::collections::{HashMap, HashSet};
3use std::ffi::OsString;
4use std::fs::{self, create_dir_all};
5use std::hash::{DefaultHasher, Hash, Hasher};
6use std::io::prelude::*;
7use std::process::{Child, Command, ExitStatus, Output, Stdio};
8use std::{env, fmt, io, iter, str};
9
10use build_helper::fs::remove_and_create_dir_all;
11use camino::{Utf8Path, Utf8PathBuf};
12use colored::{Color, Colorize};
13use regex::{Captures, Regex};
14use tracing::*;
15
16use crate::common::{
17    CompareMode, Config, Debugger, ForcePassMode, PassFailMode, RunResult, TestMode, TestPaths,
18    TestSuite, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG,
19    UI_WINDOWS_SVG, expected_output_path, incremental_dir, output_base_dir, output_base_name,
20};
21use crate::directives::{AuxCrate, TestProps};
22use crate::errors::{Error, ErrorKind, load_errors};
23use crate::executor::TestVariant;
24use crate::output_capture::ConsoleOut;
25use crate::read2::{Truncated, read2_abbreviated};
26use crate::runtest::compute_diff::{DiffLine, diff_by_lines, make_diff, write_diff};
27use crate::util::{ArgFileCommand, Utf8PathBufExt, add_dylib_path, static_regex};
28use crate::{json, stamp_file_path};
29
30// Helper modules that implement test running logic for each test suite.
31// tidy-alphabetical-start
32mod assembly;
33mod codegen;
34mod codegen_units;
35mod coverage;
36mod crashes;
37mod debuginfo;
38mod incremental;
39mod js_doc;
40mod mir_opt;
41mod pretty;
42mod run_make;
43mod rustdoc;
44mod rustdoc_json;
45mod ui;
46// tidy-alphabetical-end
47
48mod compute_diff;
49mod debugger;
50#[cfg(test)]
51mod tests;
52
53const FAKE_SRC_BASE: &str = "fake-test-src-base";
54
55#[cfg(windows)]
56fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
57    use std::sync::Mutex;
58
59    use windows::Win32::System::Diagnostics::Debug::{
60        SEM_FAILCRITICALERRORS, SEM_NOGPFAULTERRORBOX, SetErrorMode,
61    };
62
63    static LOCK: Mutex<()> = Mutex::new(());
64
65    // Error mode is a global variable, so lock it so only one thread will change it
66    let _lock = LOCK.lock().unwrap();
67
68    // Tell Windows to not show any UI on errors (such as terminating abnormally). This is important
69    // for running tests, since some of them use abnormal termination by design. This mode is
70    // inherited by all child processes.
71    //
72    // Note that `run-make` tests require `SEM_FAILCRITICALERRORS` in addition to suppress Windows
73    // Error Reporting (WER) error dialogues that come from "critical failures" such as missing
74    // DLLs.
75    //
76    // See <https://github.com/rust-lang/rust/issues/132092> and
77    // <https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode?redirectedfrom=MSDN>.
78    unsafe {
79        // read inherited flags
80        let old_mode = SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
81        SetErrorMode(old_mode | SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS);
82        let r = f();
83        SetErrorMode(old_mode);
84        r
85    }
86}
87
88#[cfg(not(windows))]
89fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
90    f()
91}
92
93/// The platform-specific library name
94fn get_lib_name(name: &str, aux_type: AuxType, wasm_proc_macros: bool) -> Option<String> {
95    match aux_type {
96        AuxType::Bin => None,
97        // In some cases (e.g. MUSL), we build a static
98        // library, rather than a dynamic library.
99        // In this case, the only path we can pass
100        // with '--extern-meta' is the '.rlib' file
101        AuxType::Lib => Some(format!("lib{name}.rlib")),
102        // FIXME maybe use `rustc --print file-names` instead?
103        AuxType::ProcMacro if wasm_proc_macros => Some(format!("{name}.wasm")),
104        AuxType::Dylib | AuxType::ProcMacro => Some(dylib_name(name)),
105    }
106}
107
108fn dylib_name(name: &str) -> String {
109    format!("{}{name}.{}", std::env::consts::DLL_PREFIX, std::env::consts::DLL_EXTENSION)
110}
111
112pub(crate) fn run(
113    config: &Config,
114    stdout: &dyn ConsoleOut,
115    stderr: &dyn ConsoleOut,
116    testpaths: &TestPaths,
117    variant: &TestVariant,
118) {
119    match &*config.target {
120        "arm-linux-androideabi"
121        | "armv7-linux-androideabi"
122        | "thumbv7neon-linux-androideabi"
123        | "aarch64-linux-android" => {
124            if !config.adb_device_status {
125                panic!("android device not available");
126            }
127        }
128        _ => {}
129    }
130
131    if config.verbose {
132        // We're going to be dumping a lot of info. Start on a new line.
133        write!(stdout, "\n\n");
134    }
135    debug!("running {}", testpaths.file);
136    let mut props = TestProps::from_file(&testpaths.file, variant.revision(), &config);
137
138    // For non-incremental (i.e. regular UI) tests, the incremental directory
139    // takes into account the revision name, since the revisions are independent
140    // of each other and can race.
141    if props.incremental {
142        props.incremental_dir = Some(incremental_dir(&config, testpaths, variant));
143    }
144
145    let cx = TestCx { config: &config, stdout, stderr, props: &props, testpaths, variant };
146
147    if let Err(e) = create_dir_all(&cx.output_base_dir()) {
148        panic!("failed to create output base directory {}: {e}", cx.output_base_dir());
149    }
150
151    if props.incremental {
152        cx.init_incremental_test();
153    }
154
155    if config.mode == TestMode::Incremental {
156        // Incremental tests are special because they cannot be run in
157        // parallel.
158        assert!(!props.revisions.is_empty(), "Incremental tests require revisions.");
159        for revision in &props.revisions {
160            let mut revision_props = TestProps::from_file(&testpaths.file, Some(revision), &config);
161            revision_props.incremental_dir = props.incremental_dir.clone();
162            let rev_cx = TestCx {
163                config: &config,
164                stdout,
165                stderr,
166                props: &revision_props,
167                testpaths,
168                variant: &TestVariant {
169                    revision: Some(revision.clone()),
170                    debugger: variant.debugger,
171                },
172            };
173            rev_cx.run_revision();
174        }
175    } else {
176        cx.run_revision();
177    }
178
179    cx.create_stamp();
180}
181
182pub(crate) fn compute_stamp_hash(config: &Config, variant: &TestVariant) -> String {
183    let mut hash = DefaultHasher::new();
184    config.stage_id.hash(&mut hash);
185    config.run.hash(&mut hash);
186    config.edition.hash(&mut hash);
187
188    match variant.debugger {
189        Some(Debugger::Cdb) => {
190            config.cdb.hash(&mut hash);
191        }
192
193        Some(Debugger::Gdb) => {
194            config.gdb.hash(&mut hash);
195            env::var_os("PATH").hash(&mut hash);
196            env::var_os("PYTHONPATH").hash(&mut hash);
197        }
198
199        Some(Debugger::Lldb) => {
200            // LLDB debuginfo tests now use LLDB's embedded Python, with an
201            // explicit PYTHONPATH, so they don't depend on `--python` or
202            // the ambient PYTHONPATH.
203            config.lldb.hash(&mut hash);
204            env::var_os("PATH").hash(&mut hash);
205        }
206
207        None => {}
208    }
209
210    if config.mode == TestMode::Ui {
211        config.force_pass_mode.hash(&mut hash);
212    }
213
214    format!("{:x}", hash.finish())
215}
216
217#[derive(Copy, Clone, Debug)]
218struct TestCx<'test> {
219    config: &'test Config,
220    stdout: &'test dyn ConsoleOut,
221    stderr: &'test dyn ConsoleOut,
222    props: &'test TestProps,
223    testpaths: &'test TestPaths,
224    variant: &'test TestVariant,
225}
226
227enum ReadFrom {
228    Path,
229    Stdin(String),
230}
231
232enum TestOutput {
233    Compile,
234    Run,
235}
236
237/// Will this test be executed? Should we use `make_exe_name`?
238#[derive(Copy, Clone, PartialEq)]
239enum WillExecute {
240    Yes,
241    No,
242    Disabled,
243}
244
245/// What value should be passed to `--emit`?
246#[derive(Copy, Clone)]
247enum Emit {
248    None,
249    Metadata,
250    LlvmIr,
251    Mir,
252    Asm,
253    LinkArgsAsm,
254}
255
256/// Indicates whether we are using `rustc` or `rustdoc` to compile an input file.
257#[derive(Clone, Copy, Debug, PartialEq, Eq)]
258enum CompilerKind {
259    Rustc,
260    Rustdoc,
261}
262
263impl<'test> TestCx<'test> {
264    /// Code executed for each revision in turn (or, if there are no
265    /// revisions, exactly once, with revision == None).
266    fn run_revision(&self) {
267        // Run the test multiple times if requested.
268        // This is useful for catching flaky tests under the parallel frontend.
269        for _ in 0..self.config.iteration_count {
270            match self.config.mode {
271                TestMode::Pretty => self.run_pretty_test(),
272                TestMode::DebugInfo => self.run_debuginfo_test(),
273                TestMode::Codegen => self.run_codegen_test(),
274                TestMode::RustdocHtml => self.run_rustdoc_html_test(),
275                TestMode::RustdocJson => self.run_rustdoc_json_test(),
276                TestMode::CodegenUnits => self.run_codegen_units_test(),
277                TestMode::Incremental => self.run_incremental_test(),
278                TestMode::RunMake => self.run_rmake_test(),
279                TestMode::Ui => self.run_ui_test(),
280                TestMode::MirOpt => self.run_mir_opt_test(),
281                TestMode::Assembly => self.run_assembly_test(),
282                TestMode::RustdocJs => self.run_rustdoc_js_test(),
283                TestMode::CoverageMap => self.run_coverage_map_test(), // see self::coverage
284                TestMode::CoverageRun => self.run_coverage_run_test(), // see self::coverage
285                TestMode::Crashes => self.run_crash_test(),
286            }
287        }
288    }
289
290    /// Returns the pass/fail expectation of this UI test
291    /// (e.g. `//@ check-pass` or `//@ build-fail`),
292    /// possibly modified by an explicit `--pass=check` on the command-line.
293    fn effective_pass_fail_mode(&self) -> Option<PassFailMode> {
294        assert_eq!(self.config.mode, TestMode::Ui);
295        // UI tests always have a pass/fail mode, but their auxiliary crates never have one.
296        let declared = self.props.pass_fail_mode?;
297
298        // Specifying `--pass` only overrides `//@ pass-*` modes, and only if
299        // the test doesn't opt out with `//@ no-pass-override`.
300        if let Some(force_pass_mode) = self.config.force_pass_mode
301            && !self.props.no_pass_override
302            && declared.is_pass()
303        {
304            match force_pass_mode {
305                ForcePassMode::Check => Some(PassFailMode::CheckPass),
306                ForcePassMode::Build => Some(PassFailMode::BuildPass),
307                ForcePassMode::Run => Some(PassFailMode::RunPass),
308            }
309        } else {
310            Some(declared)
311        }
312    }
313
314    fn run_if_enabled(&self) -> WillExecute {
315        if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled }
316    }
317
318    fn check_if_test_should_compile(&self, pass_fail: PassFailMode, proc_res: &ProcRes) {
319        assert_eq!(self.config.mode, TestMode::Ui);
320
321        let should_compile_successfully = match pass_fail {
322            PassFailMode::CheckFail | PassFailMode::BuildFail => false,
323
324            PassFailMode::CheckPass
325            | PassFailMode::BuildPass
326            | PassFailMode::RunFail
327            | PassFailMode::RunCrash
328            | PassFailMode::RunFailOrCrash
329            | PassFailMode::RunPass => true,
330        };
331
332        if should_compile_successfully {
333            if !proc_res.status.success() {
334                if pass_fail == PassFailMode::CheckPass
335                    && self.effective_pass_fail_mode() == Some(PassFailMode::BuildFail)
336                {
337                    // A `build-fail` test needs to `check-pass`.
338                    self.fatal_proc_rec(
339                        "`build-fail` test is required to pass check build, but check build failed",
340                        proc_res,
341                    );
342                } else {
343                    self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res);
344                }
345            }
346        } else {
347            if proc_res.status.success() {
348                let err = &format!("{} test did not emit an error", self.config.mode);
349                let extra_note = Some(
350                    "note: by default, ui tests are expected not to compile.\nhint: use check-pass, build-pass, or run-pass directive to change this behavior.",
351                );
352                self.fatal_proc_rec_general(err, extra_note, proc_res, || ());
353            }
354
355            if !self.props.dont_check_failure_status {
356                self.check_correct_failure_status(proc_res);
357            }
358        }
359    }
360
361    fn get_output(&self, proc_res: &ProcRes) -> String {
362        if self.props.check_stdout {
363            format!("{}{}", proc_res.stdout, proc_res.stderr)
364        } else {
365            proc_res.stderr.clone()
366        }
367    }
368
369    fn check_correct_failure_status(&self, proc_res: &ProcRes) {
370        let expected_status = Some(self.props.failure_status.unwrap_or(1));
371        let received_status = proc_res.status.code();
372
373        if expected_status != received_status {
374            self.fatal_proc_rec(
375                &format!(
376                    "Error: expected failure status ({:?}) but received status {:?}.",
377                    expected_status, received_status
378                ),
379                proc_res,
380            );
381        }
382    }
383
384    /// Runs a [`ArgFileCommand`] and waits for it to finish, then converts its exit
385    /// status and output streams into a [`ProcRes`].
386    ///
387    /// The command might have succeeded or failed; it is the caller's
388    /// responsibility to check the exit status and take appropriate action.
389    ///
390    /// # Panics
391    /// Panics if the command couldn't be executed at all
392    /// (e.g. because the executable could not be found).
393    #[must_use = "caller should check whether the command succeeded"]
394    fn run_command_to_procres(&self, cmd: ArgFileCommand) -> ProcRes {
395        let (mut cmd, _arg_file) = cmd.build().unwrap();
396        let output = cmd
397            .output()
398            .unwrap_or_else(|e| self.fatal(&format!("failed to exec `{cmd:?}` because: {e}")));
399
400        let proc_res = ProcRes {
401            status: output.status,
402            stdout: String::from_utf8(output.stdout).unwrap(),
403            stderr: String::from_utf8(output.stderr).unwrap(),
404            truncated: Truncated::No,
405            cmdline: format!("{cmd:?}"),
406        };
407        self.dump_output(
408            self.config.verbose || !proc_res.status.success(),
409            &cmd.get_program().to_string_lossy(),
410            &proc_res.stdout,
411            &proc_res.stderr,
412        );
413
414        proc_res
415    }
416
417    fn print_source(&self, read_from: ReadFrom, pretty_type: &str) -> ProcRes {
418        let aux_dir = self.aux_output_dir_name();
419        let input: &str = match read_from {
420            ReadFrom::Stdin(_) => "-",
421            ReadFrom::Path => self.testpaths.file.as_str(),
422        };
423
424        let mut rustc = Command::new(&self.config.rustc_path);
425
426        self.build_all_auxiliary(&self.aux_output_dir(), &mut rustc);
427
428        rustc
429            .arg(input)
430            .args(&["-Z", &format!("unpretty={}", pretty_type)])
431            .arg("-Zunstable-options")
432            .args(&["--target", &self.config.target])
433            .arg("-L")
434            .arg(&aux_dir)
435            .arg("-A")
436            .arg("internal_features")
437            .args(&self.props.compile_flags)
438            .envs(self.props.rustc_env.clone());
439        self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
440
441        let src = match read_from {
442            ReadFrom::Stdin(src) => Some(src),
443            ReadFrom::Path => None,
444        };
445
446        self.compose_and_run(
447            rustc,
448            self.config.host_compile_lib_path.as_path(),
449            Some(aux_dir.as_path()),
450            src,
451        )
452    }
453
454    fn compare_source(&self, expected: &str, actual: &str) {
455        if expected != actual {
456            self.fatal(&format!(
457                "pretty-printed source does not match expected source\n\
458                 expected:\n\
459                 ------------------------------------------\n\
460                 {}\n\
461                 ------------------------------------------\n\
462                 actual:\n\
463                 ------------------------------------------\n\
464                 {}\n\
465                 ------------------------------------------\n\
466                 diff:\n\
467                 ------------------------------------------\n\
468                 {}\n",
469                expected,
470                actual,
471                write_diff(expected, actual, 3),
472            ));
473        }
474    }
475
476    fn set_revision_flags(&self, cmd: &mut Command) {
477        // Normalize revisions to be lowercase and replace `-`s with `_`s.
478        // Otherwise the `--cfg` flag is not valid.
479        let normalize_revision = |revision: &str| revision.to_lowercase().replace("-", "_");
480
481        if let Some(revision) = self.variant.revision() {
482            let normalized_revision = normalize_revision(revision);
483            let cfg_arg = ["--cfg", &normalized_revision];
484            let arg = format!("--cfg={normalized_revision}");
485            // Handle if compile_flags is length 1
486            let contains_arg =
487                self.props.compile_flags.iter().any(|considered_arg| *considered_arg == arg);
488            let contains_cfg_arg = self.props.compile_flags.windows(2).any(|args| args == cfg_arg);
489            if contains_arg || contains_cfg_arg {
490                error!(
491                    "redundant cfg argument `{normalized_revision}` is already created by the \
492                    revision"
493                );
494                panic!("redundant cfg argument");
495            }
496            if self.config.builtin_cfg_names().contains(&normalized_revision) {
497                error!("revision `{normalized_revision}` collides with a built-in cfg");
498                panic!("revision collides with built-in cfg");
499            }
500            cmd.args(cfg_arg);
501        }
502
503        if !self.props.no_auto_check_cfg {
504            let mut check_cfg = String::with_capacity(25);
505
506            // Generate `cfg(FALSE, REV1, ..., REVN)` (for all possible revisions)
507            //
508            // For compatibility reason we consider the `FALSE` cfg to be expected
509            // since it is extensively used in the testsuite, as well as the `test`
510            // cfg since we have tests that uses it.
511            check_cfg.push_str("cfg(test,FALSE");
512            for revision in &self.props.revisions {
513                check_cfg.push(',');
514                check_cfg.push_str(&normalize_revision(revision));
515            }
516            check_cfg.push(')');
517
518            cmd.args(&["--check-cfg", &check_cfg]);
519        }
520    }
521
522    fn typecheck_source(&self, src: String) -> ProcRes {
523        let mut rustc = Command::new(&self.config.rustc_path);
524
525        let out_dir = self.output_base_name().with_extension("pretty-out");
526        remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
527            panic!("failed to remove and recreate output directory `{out_dir}`: {e}")
528        });
529
530        let target = if self.props.force_host { &*self.config.host } else { &*self.config.target };
531
532        let aux_dir = self.aux_output_dir_name();
533
534        rustc
535            .arg("-")
536            .arg("-Zno-codegen")
537            .arg("-Zunstable-options")
538            .arg("--out-dir")
539            .arg(&out_dir)
540            .arg(&format!("--target={}", target))
541            .arg("-L")
542            // FIXME(jieyouxu): this search path seems questionable. Is this intended for
543            // `rust_test_helpers` in ui tests?
544            .arg(&self.config.build_test_suite_root)
545            .arg("-L")
546            .arg(aux_dir)
547            .arg("-A")
548            .arg("internal_features");
549        self.set_revision_flags(&mut rustc);
550        self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
551        rustc.args(&self.props.compile_flags);
552
553        self.compose_and_run_compiler(rustc, Some(src))
554    }
555
556    fn maybe_add_external_args(&self, cmd: &mut Command, args: &Vec<String>) {
557        // Filter out the arguments that should not be added by runtest here.
558        //
559        // Notable use-cases are: do not add our optimisation flag if
560        // `compile-flags: -Copt-level=x` and similar for debug-info level as well.
561        const OPT_FLAGS: &[&str] = &["-O", "-Copt-level=", /*-C<space>*/ "opt-level="];
562        const DEBUG_FLAGS: &[&str] = &["-g", "-Cdebuginfo=", /*-C<space>*/ "debuginfo="];
563
564        // FIXME: ideally we would "just" check the `cmd` itself, but it does not allow inspecting
565        // its arguments. They need to be collected separately. For now I cannot be bothered to
566        // implement this the "right" way.
567        let have_opt_flag =
568            self.props.compile_flags.iter().any(|arg| OPT_FLAGS.iter().any(|f| arg.starts_with(f)));
569        let have_debug_flag = self
570            .props
571            .compile_flags
572            .iter()
573            .any(|arg| DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)));
574
575        for arg in args {
576            if OPT_FLAGS.iter().any(|f| arg.starts_with(f)) && have_opt_flag {
577                continue;
578            }
579            if DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)) && have_debug_flag {
580                continue;
581            }
582            cmd.arg(arg);
583        }
584    }
585
586    /// Check `error-pattern` and `regex-error-pattern` directives.
587    fn check_all_error_patterns(&self, output_to_check: &str, proc_res: &ProcRes) {
588        let mut missing_patterns: Vec<String> = Vec::new();
589        self.check_error_patterns(output_to_check, &mut missing_patterns);
590        self.check_regex_error_patterns(output_to_check, proc_res, &mut missing_patterns);
591
592        if missing_patterns.is_empty() {
593            return;
594        }
595
596        if missing_patterns.len() == 1 {
597            self.fatal_proc_rec(
598                &format!("error pattern '{}' not found!", missing_patterns[0]),
599                proc_res,
600            );
601        } else {
602            for pattern in missing_patterns {
603                writeln!(
604                    self.stdout,
605                    "\n{prefix}: error pattern '{pattern}' not found!",
606                    prefix = self.error_prefix()
607                );
608            }
609            self.fatal_proc_rec("multiple error patterns not found", proc_res);
610        }
611    }
612
613    fn check_error_patterns(&self, output_to_check: &str, missing_patterns: &mut Vec<String>) {
614        debug!("check_error_patterns");
615        for pattern in &self.props.error_patterns {
616            if output_to_check.contains(pattern.trim()) {
617                debug!("found error pattern {}", pattern);
618            } else {
619                missing_patterns.push(pattern.to_string());
620            }
621        }
622    }
623
624    fn check_regex_error_patterns(
625        &self,
626        output_to_check: &str,
627        proc_res: &ProcRes,
628        missing_patterns: &mut Vec<String>,
629    ) {
630        debug!("check_regex_error_patterns");
631
632        for pattern in &self.props.regex_error_patterns {
633            let pattern = pattern.trim();
634            let re = match Regex::new(pattern) {
635                Ok(re) => re,
636                Err(err) => {
637                    self.fatal_proc_rec(
638                        &format!("invalid regex error pattern '{}': {:?}", pattern, err),
639                        proc_res,
640                    );
641                }
642            };
643            if re.is_match(output_to_check) {
644                debug!("found regex error pattern {}", pattern);
645            } else {
646                missing_patterns.push(pattern.to_string());
647            }
648        }
649    }
650
651    fn check_forbid_output(&self, output_to_check: &str, proc_res: &ProcRes) {
652        for pat in &self.props.forbid_output {
653            if output_to_check.contains(pat) {
654                self.fatal_proc_rec("forbidden pattern found in compiler output", proc_res);
655            }
656        }
657    }
658
659    /// Check `//~ KIND message` annotations.
660    fn check_expected_errors(&self, proc_res: &ProcRes) {
661        let expected_errors = load_errors(&self.testpaths.file, self.variant.revision());
662        debug!(
663            "check_expected_errors: expected_errors={:?} proc_res.status={:?}",
664            expected_errors, proc_res.status
665        );
666        if proc_res.status.success() && expected_errors.iter().any(|x| x.kind == ErrorKind::Error) {
667            self.fatal_proc_rec("process did not return an error status", proc_res);
668        }
669
670        if self.props.known_bug {
671            if !expected_errors.is_empty() {
672                self.fatal_proc_rec(
673                    "`known_bug` tests should not have an expected error",
674                    proc_res,
675                );
676            }
677            return;
678        }
679
680        // On Windows, keep all '\' path separators to match the paths reported in the JSON output
681        // from the compiler
682        let diagnostic_file_name = if self.props.remap_src_base {
683            let mut p = Utf8PathBuf::from(FAKE_SRC_BASE);
684            p.push(&self.testpaths.relative_dir);
685            p.push(self.testpaths.file.file_name().unwrap());
686            p.to_string()
687        } else {
688            self.testpaths.file.to_string()
689        };
690
691        // Errors and warnings are always expected, other diagnostics are only expected
692        // if one of them actually occurs in the test.
693        let expected_kinds: HashSet<_> = [ErrorKind::Error, ErrorKind::Warning]
694            .into_iter()
695            .chain(expected_errors.iter().map(|e| e.kind))
696            .collect();
697
698        // Parse the JSON output from the compiler and extract out the messages.
699        let actual_errors = json::parse_output(&diagnostic_file_name, &self.get_output(proc_res))
700            .into_iter()
701            .map(|e| Error { msg: self.normalize_output(&e.msg, &[]), ..e });
702
703        let mut unexpected = Vec::new();
704        let mut unimportant = Vec::new();
705        let mut found = vec![false; expected_errors.len()];
706        for actual_error in actual_errors {
707            for pattern in &self.props.error_patterns {
708                let pattern = pattern.trim();
709                if actual_error.msg.contains(pattern) {
710                    let q = if actual_error.line_num.is_none() { "?" } else { "" };
711                    self.fatal(&format!(
712                        "error pattern '{pattern}' is found in structured \
713                         diagnostics, use `//~{q} {} {pattern}` instead",
714                        actual_error.kind,
715                    ));
716                }
717            }
718
719            let opt_index =
720                expected_errors.iter().enumerate().position(|(index, expected_error)| {
721                    !found[index]
722                        && actual_error.line_num == expected_error.line_num
723                        && actual_error.kind == expected_error.kind
724                        && actual_error.msg.contains(&expected_error.msg)
725                });
726
727            match opt_index {
728                Some(index) => {
729                    // found a match, everybody is happy
730                    assert!(!found[index]);
731                    found[index] = true;
732                }
733
734                None => {
735                    if actual_error.require_annotation
736                        && expected_kinds.contains(&actual_error.kind)
737                        && !self.props.dont_require_annotations.contains(&actual_error.kind)
738                    {
739                        unexpected.push(actual_error);
740                    } else {
741                        unimportant.push(actual_error);
742                    }
743                }
744            }
745        }
746
747        unexpected.sort_by_key(|e| (e.line_num, e.column_num));
748        unimportant.sort_by_key(|e| (e.line_num, e.column_num));
749
750        // `not_found` are sorted because `expected_errors` are sorted as they are read from file
751        // line by line.
752        let mut not_found = Vec::new();
753        // anything not yet found is a problem
754        for (index, expected_error) in expected_errors.iter().enumerate() {
755            if !found[index] {
756                not_found.push(expected_error);
757            }
758        }
759
760        if !unexpected.is_empty() || !not_found.is_empty() {
761            // Emit locations in a format that is short (relative paths) but "clickable" in editors.
762            // Also normalize path separators to `/`.
763            let file_name = self
764                .testpaths
765                .file
766                .strip_prefix(self.config.src_root.as_str())
767                .unwrap_or(&self.testpaths.file)
768                .to_string()
769                .replace(r"\", "/");
770            let line_str = |e: &Error| {
771                let line_num = e.line_num.map_or("?".to_string(), |line_num| line_num.to_string());
772                // `file:?:NUM` may be confusing to editors and unclickable.
773                let opt_col_num = match e.column_num {
774                    Some(col_num) if line_num != "?" => format!(":{col_num}"),
775                    _ => "".to_string(),
776                };
777                format!("{file_name}:{line_num}{opt_col_num}")
778            };
779            let print_error =
780                |e| writeln!(self.stdout, "{}: {}: {}", line_str(e), e.kind, e.msg.cyan());
781            let push_suggestion =
782                |suggestions: &mut Vec<_>, e: &Error, kind, line, msg, color, rank| {
783                    let mut ret = String::new();
784                    if kind {
785                        ret += &format!("{} {}", "with different kind:".color(color), e.kind);
786                    }
787                    if line {
788                        if !ret.is_empty() {
789                            ret.push(' ');
790                        }
791                        ret += &format!("{} {}", "on different line:".color(color), line_str(e));
792                    }
793                    if msg {
794                        if !ret.is_empty() {
795                            ret.push(' ');
796                        }
797                        ret +=
798                            &format!("{} {}", "with different message:".color(color), e.msg.cyan());
799                    }
800                    suggestions.push((ret, rank));
801                };
802            let show_suggestions = |mut suggestions: Vec<_>, prefix: &str, color| {
803                // Only show suggestions with the highest rank.
804                suggestions.sort_by_key(|(_, rank)| *rank);
805                if let Some(&(_, top_rank)) = suggestions.first() {
806                    for (suggestion, rank) in suggestions {
807                        if rank == top_rank {
808                            writeln!(self.stdout, "  {} {suggestion}", prefix.color(color));
809                        }
810                    }
811                }
812            };
813
814            // Fuzzy matching quality:
815            // - message and line / message and kind - great, suggested
816            // - only message - good, suggested
817            // - known line and kind - ok, suggested
818            // - only known line - meh, but suggested
819            // - others are not worth suggesting
820            if !unexpected.is_empty() {
821                writeln!(
822                    self.stdout,
823                    "\n{prefix}: {n} diagnostics reported in rustc output but not expected in test file",
824                    prefix = self.error_prefix(),
825                    n = unexpected.len(),
826                );
827                for error in &unexpected {
828                    print_error(error);
829                    let mut suggestions = Vec::new();
830                    for candidate in &not_found {
831                        let kind_mismatch = candidate.kind != error.kind;
832                        let mut push_red_suggestion = |line, msg, rank| {
833                            push_suggestion(
834                                &mut suggestions,
835                                candidate,
836                                kind_mismatch,
837                                line,
838                                msg,
839                                Color::Red,
840                                rank,
841                            )
842                        };
843                        if error.msg.contains(&candidate.msg) {
844                            push_red_suggestion(candidate.line_num != error.line_num, false, 0);
845                        } else if candidate.line_num.is_some()
846                            && candidate.line_num == error.line_num
847                        {
848                            push_red_suggestion(false, true, if kind_mismatch { 2 } else { 1 });
849                        }
850                    }
851
852                    show_suggestions(suggestions, "expected", Color::Red);
853                }
854            }
855            if !not_found.is_empty() {
856                writeln!(
857                    self.stdout,
858                    "\n{prefix}: {n} diagnostics expected in test file but not reported in rustc output",
859                    prefix = self.error_prefix(),
860                    n = not_found.len(),
861                );
862
863                // FIXME: Ideally, we should check this at the place where we actually parse error annotations.
864                // it's better to use (negated) heuristic inside normalize_output if possible
865                if let Some(human_format) = self.props.compile_flags.iter().find(|flag| {
866                    // `human`, `human-unicode`, `short` will not generate JSON output
867                    flag.contains("error-format")
868                        && (flag.contains("short") || flag.contains("human"))
869                }) {
870                    let msg = format!(
871                        "tests with compile flag `{}` should not have error annotations such as `//~ ERROR`",
872                        human_format
873                    ).color(Color::Red);
874                    writeln!(self.stdout, "{}", msg);
875                }
876
877                for error in &not_found {
878                    print_error(error);
879                    let mut suggestions = Vec::new();
880                    for candidate in unexpected.iter().chain(&unimportant) {
881                        let kind_mismatch = candidate.kind != error.kind;
882                        let mut push_green_suggestion = |line, msg, rank| {
883                            push_suggestion(
884                                &mut suggestions,
885                                candidate,
886                                kind_mismatch,
887                                line,
888                                msg,
889                                Color::Green,
890                                rank,
891                            )
892                        };
893                        if candidate.msg.contains(&error.msg) {
894                            push_green_suggestion(candidate.line_num != error.line_num, false, 0);
895                        } else if candidate.line_num.is_some()
896                            && candidate.line_num == error.line_num
897                        {
898                            push_green_suggestion(false, true, if kind_mismatch { 2 } else { 1 });
899                        }
900                    }
901
902                    show_suggestions(suggestions, "reported", Color::Green);
903                }
904            }
905            panic!(
906                "errors differ from expected\nstatus: {}\ncommand: {}\n",
907                proc_res.status, proc_res.cmdline
908            );
909        }
910    }
911
912    fn compile_test(&self, will_execute: WillExecute, emit: Emit) -> ProcRes {
913        self.compile_test_general(will_execute, emit, Vec::new())
914    }
915
916    fn compile_test_general(
917        &self,
918        will_execute: WillExecute,
919        emit: Emit,
920        passes: Vec<String>,
921    ) -> ProcRes {
922        let compiler_kind = self.compiler_kind_for_non_aux();
923
924        // Only use `make_exe_name` when the test ends up being executed.
925        let output_file = match will_execute {
926            WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()),
927            WillExecute::No | WillExecute::Disabled => {
928                TargetLocation::ThisDirectory(self.output_base_dir())
929            }
930        };
931
932        let allow_unused = match self.config.mode {
933            TestMode::Ui => {
934                // UI tests tend to have tons of unused code as
935                // it's just testing various pieces of the compile, but we don't
936                // want to actually assert warnings about all this code. Instead
937                // let's just ignore unused code warnings by defaults and tests
938                // can turn it back on if needed.
939                if compiler_kind == CompilerKind::Rustc
940                    // Note that we use the declared pass mode here as we don't want
941                    // to set unused to allow if we've overridden the pass mode
942                    // via command line flags.
943                    // FIXME(Zalathar): We should probably also warn in run-fail/crash
944                    // tests, but that requires changes to some existing tests.
945                    && self.props.pass_fail_mode != Some(PassFailMode::RunPass)
946                {
947                    AllowUnused::Yes
948                } else {
949                    AllowUnused::No
950                }
951            }
952            TestMode::Incremental => AllowUnused::Yes,
953            _ => AllowUnused::No,
954        };
955
956        let rustc = self.make_compile_args(
957            compiler_kind,
958            &self.testpaths.file,
959            output_file,
960            emit,
961            allow_unused,
962            LinkToAux::Yes,
963            passes,
964        );
965
966        self.compose_and_run_compiler(rustc, None)
967    }
968
969    /// `root_out_dir` and `root_testpaths` refer to the parameters of the actual test being run.
970    /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths.
971    fn document(&self, root_out_dir: &Utf8Path, kind: DocKind) -> ProcRes {
972        self.document_inner(&self.testpaths.file, root_out_dir, kind)
973    }
974
975    /// Like `document`, but takes an explicit `file_to_doc` argument so that
976    /// it can also be used for documenting auxiliaries, in addition to
977    /// documenting the main test file.
978    fn document_inner(
979        &self,
980        file_to_doc: &Utf8Path,
981        root_out_dir: &Utf8Path,
982        kind: DocKind,
983    ) -> ProcRes {
984        if self.props.build_aux_docs {
985            assert_eq!(kind, DocKind::Html, "build-aux-docs only make sense for html output");
986
987            for rel_ab in &self.props.aux.builds {
988                let aux_path = self.resolve_aux_path(rel_ab);
989                let props_for_aux =
990                    self.props.from_aux_file(&aux_path, self.variant.revision(), self.config);
991                let aux_cx = TestCx {
992                    config: self.config,
993                    stdout: self.stdout,
994                    stderr: self.stderr,
995                    props: &props_for_aux,
996                    testpaths: self.testpaths,
997                    variant: self.variant,
998                };
999                // Create the directory for the stdout/stderr files.
1000                create_dir_all(aux_cx.output_base_dir()).unwrap();
1001                let auxres = aux_cx.document_inner(&aux_path, &root_out_dir, kind);
1002                if !auxres.status.success() {
1003                    return auxres;
1004                }
1005            }
1006        }
1007
1008        let aux_dir = self.aux_output_dir_name();
1009
1010        let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed");
1011
1012        // actual --out-dir given to the auxiliary or test, as opposed to the root out dir for the entire
1013        // test
1014        let out_dir: Cow<'_, Utf8Path> = if self.props.unique_doc_out_dir {
1015            let file_name = file_to_doc.file_stem().expect("file name should not be empty");
1016            let out_dir = Utf8PathBuf::from_iter([
1017                root_out_dir,
1018                Utf8Path::new("docs"),
1019                Utf8Path::new(file_name),
1020                Utf8Path::new("doc"),
1021            ]);
1022            create_dir_all(&out_dir).unwrap();
1023            Cow::Owned(out_dir)
1024        } else {
1025            Cow::Borrowed(root_out_dir)
1026        };
1027
1028        let mut rustdoc = Command::new(rustdoc_path);
1029        let current_dir = self.output_base_dir();
1030        rustdoc.current_dir(current_dir);
1031        rustdoc
1032            .arg("-L")
1033            .arg(self.config.target_run_lib_path.as_path())
1034            .arg("-L")
1035            .arg(aux_dir)
1036            .arg("-o")
1037            .arg(out_dir.as_ref())
1038            .arg("--deny")
1039            .arg("warnings")
1040            .arg(file_to_doc)
1041            .arg("-A")
1042            .arg("internal_features")
1043            // FIXME(#160895): While the new solver is enabled by default on nightly,
1044            // we don't want to use it in our tests for now.
1045            .arg("-Znext-solver=coherence")
1046            .args(&self.props.compile_flags)
1047            .args(&self.props.doc_flags);
1048
1049        match kind {
1050            DocKind::Html => {}
1051            DocKind::Json => {
1052                rustdoc.arg("--output-format").arg("json");
1053            }
1054        }
1055
1056        // Both JSON output and `--disable-minification` are unstable rustdoc options.
1057        if matches!(kind, DocKind::Json) || self.config.disable_minification {
1058            rustdoc.arg("-Zunstable-options");
1059        }
1060        if self.config.disable_minification {
1061            rustdoc.arg("--disable-minification");
1062        }
1063
1064        if let Some(ref linker) = self.config.target_linker {
1065            rustdoc.arg(format!("-Clinker={}", linker));
1066        }
1067
1068        self.compose_and_run_compiler(rustdoc, None)
1069    }
1070
1071    fn exec_compiled_test(&self) -> ProcRes {
1072        self.exec_compiled_test_general(&[], true)
1073    }
1074
1075    fn exec_compiled_test_general(
1076        &self,
1077        env_extra: &[(&str, &str)],
1078        delete_after_success: bool,
1079    ) -> ProcRes {
1080        let prepare_env = |cmd: &mut Command| {
1081            for (key, val) in &self.props.exec_env {
1082                cmd.env(key, val);
1083            }
1084            for (key, val) in env_extra {
1085                cmd.env(key, val);
1086            }
1087
1088            for key in &self.props.unset_exec_env {
1089                cmd.env_remove(key);
1090            }
1091        };
1092
1093        let proc_res = match &*self.config.target {
1094            // This is pretty similar to below, we're transforming:
1095            //
1096            // ```text
1097            // program arg1 arg2
1098            // ```
1099            //
1100            // into
1101            //
1102            // ```text
1103            // remote-test-client run program 2 support-lib.so support-lib2.so arg1 arg2
1104            // ```
1105            //
1106            // The test-client program will upload `program` to the emulator along with all other
1107            // support libraries listed (in this case `support-lib.so` and `support-lib2.so`. It
1108            // will then execute the program on the emulator with the arguments specified (in the
1109            // environment we give the process) and then report back the same result.
1110            _ if self.config.remote_test_client.is_some() => {
1111                let aux_dir = self.aux_output_dir_name();
1112                let ProcArgs { prog, args } = self.make_run_args();
1113                let mut support_libs = Vec::new();
1114                if let Ok(entries) = aux_dir.read_dir() {
1115                    for entry in entries {
1116                        let entry = entry.unwrap();
1117                        if !entry.path().is_file() {
1118                            continue;
1119                        }
1120                        support_libs.push(entry.path());
1121                    }
1122                }
1123                let mut test_client =
1124                    Command::new(self.config.remote_test_client.as_ref().unwrap());
1125                test_client
1126                    .args(&["run", &support_libs.len().to_string()])
1127                    .arg(&prog)
1128                    .args(support_libs)
1129                    .args(args);
1130
1131                prepare_env(&mut test_client);
1132
1133                self.compose_and_run(
1134                    test_client,
1135                    self.config.target_run_lib_path.as_path(),
1136                    Some(aux_dir.as_path()),
1137                    None,
1138                )
1139            }
1140            _ if self.config.target.contains("vxworks") => {
1141                let aux_dir = self.aux_output_dir_name();
1142                let ProcArgs { prog, args } = self.make_run_args();
1143                let mut wr_run = Command::new("wr-run");
1144                wr_run.args(&[&prog]).args(args);
1145
1146                prepare_env(&mut wr_run);
1147
1148                self.compose_and_run(
1149                    wr_run,
1150                    self.config.target_run_lib_path.as_path(),
1151                    Some(aux_dir.as_path()),
1152                    None,
1153                )
1154            }
1155            _ => {
1156                let aux_dir = self.aux_output_dir_name();
1157                let ProcArgs { prog, args } = self.make_run_args();
1158                let mut program = Command::new(&prog);
1159                program.args(args).current_dir(&self.output_base_dir());
1160
1161                prepare_env(&mut program);
1162
1163                self.compose_and_run(
1164                    program,
1165                    self.config.target_run_lib_path.as_path(),
1166                    Some(aux_dir.as_path()),
1167                    None,
1168                )
1169            }
1170        };
1171
1172        if delete_after_success && proc_res.status.success() {
1173            // delete the executable after running it to save space.
1174            // it is ok if the deletion failed.
1175            let _ = fs::remove_file(self.make_exe_name());
1176        }
1177
1178        proc_res
1179    }
1180
1181    /// For each `aux-build: foo/bar` annotation, we check to find the file in an `auxiliary`
1182    /// directory relative to the test itself (not any intermediate auxiliaries).
1183    fn resolve_aux_path(&self, relative_aux_path: &str) -> Utf8PathBuf {
1184        let aux_path = self
1185            .testpaths
1186            .file
1187            .parent()
1188            .expect("test file path has no parent")
1189            .join("auxiliary")
1190            .join(relative_aux_path);
1191        if !aux_path.exists() {
1192            self.fatal(&format!(
1193                "auxiliary source file `{relative_aux_path}` not found at `{aux_path}`"
1194            ));
1195        }
1196
1197        aux_path
1198    }
1199
1200    fn is_vxworks_pure_static(&self) -> bool {
1201        if self.config.target.contains("vxworks") {
1202            match env::var("RUST_VXWORKS_TEST_DYLINK") {
1203                Ok(s) => s != "1",
1204                _ => true,
1205            }
1206        } else {
1207            false
1208        }
1209    }
1210
1211    fn is_vxworks_pure_dynamic(&self) -> bool {
1212        self.config.target.contains("vxworks") && !self.is_vxworks_pure_static()
1213    }
1214
1215    fn has_aux_dir(&self) -> bool {
1216        !self.props.aux.builds.is_empty()
1217            || !self.props.aux.crates.is_empty()
1218            || !self.props.aux.proc_macros.is_empty()
1219    }
1220
1221    fn aux_output_dir(&self) -> Utf8PathBuf {
1222        let aux_dir = self.aux_output_dir_name();
1223
1224        if !self.props.aux.builds.is_empty() {
1225            remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| {
1226                panic!("failed to remove and recreate output directory `{aux_dir}`: {e}")
1227            });
1228        }
1229
1230        if !self.props.aux.bins.is_empty() {
1231            let aux_bin_dir = self.aux_bin_output_dir_name();
1232            remove_and_create_dir_all(&aux_dir).unwrap_or_else(|e| {
1233                panic!("failed to remove and recreate output directory `{aux_dir}`: {e}")
1234            });
1235            remove_and_create_dir_all(&aux_bin_dir).unwrap_or_else(|e| {
1236                panic!("failed to remove and recreate output directory `{aux_bin_dir}`: {e}")
1237            });
1238        }
1239
1240        aux_dir
1241    }
1242
1243    fn build_all_auxiliary(&self, aux_dir: &Utf8Path, rustc: &mut Command) {
1244        for rel_ab in &self.props.aux.builds {
1245            self.build_auxiliary(rel_ab, &aux_dir, None);
1246        }
1247
1248        for rel_ab in &self.props.aux.bins {
1249            self.build_auxiliary(rel_ab, &aux_dir, Some(AuxType::Bin));
1250        }
1251
1252        let path_to_crate_name = |path: &str| -> String {
1253            path.rsplit_once('/')
1254                .map_or(path, |(_, tail)| tail)
1255                .trim_end_matches(".rs")
1256                .replace('-', "_")
1257        };
1258
1259        let add_extern = |rustc: &mut Command,
1260                          extern_modifiers: Option<&str>,
1261                          aux_name: &str,
1262                          aux_path: &str,
1263                          aux_type: AuxType| {
1264            let lib_name =
1265                get_lib_name(&path_to_crate_name(aux_path), aux_type, self.config.wasm_proc_macros);
1266            if let Some(lib_name) = lib_name {
1267                let modifiers_and_name = match extern_modifiers {
1268                    Some(modifiers) => format!("{modifiers}:{aux_name}"),
1269                    None => aux_name.to_string(),
1270                };
1271                rustc.arg("--extern").arg(format!("{modifiers_and_name}={aux_dir}/{lib_name}"));
1272            }
1273        };
1274
1275        for AuxCrate { extern_modifiers, name, path } in &self.props.aux.crates {
1276            let aux_type = self.build_auxiliary(&path, &aux_dir, None);
1277            add_extern(rustc, extern_modifiers.as_deref(), name, path, aux_type);
1278        }
1279
1280        for proc_macro in &self.props.aux.proc_macros {
1281            self.build_auxiliary(&proc_macro.path, &aux_dir, Some(AuxType::ProcMacro));
1282            let crate_name = path_to_crate_name(&proc_macro.path);
1283            add_extern(
1284                rustc,
1285                proc_macro.extern_modifiers.as_deref(),
1286                &crate_name,
1287                &proc_macro.path,
1288                AuxType::ProcMacro,
1289            );
1290        }
1291
1292        // Build any `//@ aux-codegen-backend`, and pass the resulting library
1293        // to `-Zcodegen-backend` when compiling the test file.
1294        if let Some(aux_file) = &self.props.aux.codegen_backend {
1295            let aux_type = self.build_auxiliary(aux_file, aux_dir, None);
1296            if let Some(lib_name) = get_lib_name(
1297                aux_file.trim_end_matches(".rs"),
1298                aux_type,
1299                self.config.wasm_proc_macros,
1300            ) {
1301                let lib_path = aux_dir.join(&lib_name);
1302                rustc.arg(format!("-Zcodegen-backend={}", lib_path));
1303            }
1304        }
1305    }
1306
1307    /// `root_testpaths` refers to the path of the original test. the auxiliary and the test with an
1308    /// aux-build have the same `root_testpaths`.
1309    fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
1310        if self.props.add_minicore {
1311            let minicore_path = self.build_minicore();
1312            rustc.arg("--extern");
1313            rustc.arg(&format!("minicore={}", minicore_path));
1314        }
1315
1316        let aux_dir = self.aux_output_dir();
1317        self.build_all_auxiliary(&aux_dir, &mut rustc);
1318
1319        rustc.envs(self.props.rustc_env.clone());
1320        self.props.unset_rustc_env.iter().fold(&mut rustc, Command::env_remove);
1321        self.compose_and_run(
1322            rustc,
1323            self.config.host_compile_lib_path.as_path(),
1324            Some(aux_dir.as_path()),
1325            input,
1326        )
1327    }
1328
1329    /// Builds `minicore`. Returns the path to the minicore rlib within the base test output
1330    /// directory.
1331    fn build_minicore(&self) -> Utf8PathBuf {
1332        let output_file_path = self.output_base_dir().join("libminicore.rlib");
1333        let mut rustc = self.make_compile_args(
1334            CompilerKind::Rustc,
1335            &self.config.minicore_path,
1336            TargetLocation::ThisFile(output_file_path.clone()),
1337            Emit::None,
1338            AllowUnused::Yes,
1339            LinkToAux::No,
1340            vec![],
1341        );
1342
1343        rustc.args(&["--crate-type", "rlib"]);
1344        rustc.arg("-Cpanic=abort");
1345        rustc.args(self.props.minicore_compile_flags.clone());
1346
1347        let res =
1348            self.compose_and_run(rustc, self.config.host_compile_lib_path.as_path(), None, None);
1349        if !res.status.success() {
1350            self.fatal_proc_rec(
1351                &format!("auxiliary build of {} failed to compile: ", self.config.minicore_path),
1352                &res,
1353            );
1354        }
1355
1356        output_file_path
1357    }
1358
1359    /// Builds an aux dependency.
1360    ///
1361    /// If `aux_type` is `None`, then this will determine the aux-type automatically.
1362    fn build_auxiliary(
1363        &self,
1364        source_path: &str,
1365        aux_dir: &Utf8Path,
1366        aux_type: Option<AuxType>,
1367    ) -> AuxType {
1368        let aux_path = self.resolve_aux_path(source_path);
1369        let mut aux_props =
1370            self.props.from_aux_file(&aux_path, self.variant.revision(), self.config);
1371        if aux_type == Some(AuxType::ProcMacro) {
1372            if self.config.wasm_proc_macros {
1373                aux_props.compile_flags.push("--target=wasm32-wasip2".to_owned());
1374                // Override any earlier linkers for now, otherwise we fail to build since compiletest
1375                // thinks we're building for a different target and passes its linker (if one is
1376                // configured).
1377                //
1378                // wasm32-wasip2 should in principle always be able to link with wasm-component-ld +
1379                // wasm-ld. This does mean that rust.lld needs to be enabled to build wasm-ld wrapper
1380                // around rust-lld.
1381                aux_props.compile_flags.push("-Clinker=wasm-component-ld".to_owned());
1382                aux_props.compile_flags.push(format!(
1383                    "-Clink-arg=--wasm-ld-path={}",
1384                    self.config
1385                        .sysroot_base
1386                        .join("lib/rustlib")
1387                        .join(&self.config.host)
1388                        .join("bin/gcc-ld/wasm-ld")
1389                ));
1390            } else {
1391                aux_props.force_host = true;
1392            }
1393        }
1394        let mut aux_dir = aux_dir.to_path_buf();
1395        if aux_type == Some(AuxType::Bin) {
1396            // On unix, the binary of `auxiliary/foo.rs` will be named
1397            // `auxiliary/foo` which clashes with the _dir_ `auxiliary/foo`, so
1398            // put bins in a `bin` subfolder.
1399            aux_dir.push("bin");
1400        }
1401        let aux_output = TargetLocation::ThisDirectory(aux_dir.clone());
1402        let aux_cx = TestCx {
1403            config: self.config,
1404            stdout: self.stdout,
1405            stderr: self.stderr,
1406            props: &aux_props,
1407            testpaths: self.testpaths,
1408            variant: self.variant,
1409        };
1410        // Create the directory for the stdout/stderr files.
1411        create_dir_all(aux_cx.output_base_dir()).unwrap();
1412        let mut aux_rustc = aux_cx.make_compile_args(
1413            // Always use `rustc` for aux crates, even in rustdoc tests.
1414            CompilerKind::Rustc,
1415            &aux_path,
1416            aux_output,
1417            Emit::None,
1418            AllowUnused::No,
1419            LinkToAux::No,
1420            Vec::new(),
1421        );
1422        aux_cx.build_all_auxiliary(&aux_dir, &mut aux_rustc);
1423
1424        aux_rustc.envs(aux_props.rustc_env.clone());
1425        for key in &aux_props.unset_rustc_env {
1426            aux_rustc.env_remove(key);
1427        }
1428
1429        let (aux_type, crate_type) = if aux_type == Some(AuxType::Bin) {
1430            (AuxType::Bin, Some("bin"))
1431        } else if aux_type == Some(AuxType::ProcMacro) {
1432            (AuxType::ProcMacro, Some("proc-macro"))
1433        } else if aux_type.is_some() {
1434            panic!("aux_type {aux_type:?} not expected");
1435        } else if aux_props.no_prefer_dynamic {
1436            (AuxType::Lib, None)
1437        } else if self.config.target.contains("emscripten")
1438            || (self.config.target.contains("musl")
1439                && !aux_props.force_host
1440                && !self.config.host.contains("musl"))
1441            || self.config.target.contains("wasm32")
1442            || self.config.target.contains("nvptx")
1443            || self.is_vxworks_pure_static()
1444            || self.config.target.contains("bpf")
1445            || !self.config.target_cfg().dynamic_linking
1446            || matches!(self.config.mode, TestMode::CoverageMap | TestMode::CoverageRun)
1447        {
1448            // We primarily compile all auxiliary libraries as dynamic libraries
1449            // to avoid code size bloat and large binaries as much as possible
1450            // for the test suite (otherwise including libstd statically in all
1451            // executables takes up quite a bit of space).
1452            //
1453            // For targets like MUSL or Emscripten, however, there is no support for
1454            // dynamic libraries so we just go back to building a normal library. Note,
1455            // however, that for MUSL if the library is built with `force_host` then
1456            // it's ok to be a dylib as the host should always support dylibs.
1457            //
1458            // Coverage tests want static linking by default so that coverage
1459            // mappings in auxiliary libraries can be merged into the final
1460            // executable.
1461            (AuxType::Lib, Some("lib"))
1462        } else {
1463            (AuxType::Dylib, Some("dylib"))
1464        };
1465
1466        if let Some(crate_type) = crate_type {
1467            aux_rustc.args(&["--crate-type", crate_type]);
1468        }
1469
1470        if aux_type == AuxType::ProcMacro {
1471            // For convenience, but this only works on 2018.
1472            aux_rustc.args(&["--extern", "proc_macro"]);
1473        }
1474
1475        aux_rustc.arg("-L").arg(&aux_dir);
1476
1477        if aux_props.add_minicore {
1478            let minicore_path = self.build_minicore();
1479            aux_rustc.arg("--extern");
1480            aux_rustc.arg(&format!("minicore={}", minicore_path));
1481        }
1482
1483        let auxres = aux_cx.compose_and_run(
1484            aux_rustc,
1485            aux_cx.config.host_compile_lib_path.as_path(),
1486            Some(aux_dir.as_path()),
1487            None,
1488        );
1489        if !auxres.status.success() {
1490            self.fatal_proc_rec(
1491                &format!("auxiliary build of {aux_path} failed to compile: "),
1492                &auxres,
1493            );
1494        }
1495        aux_type
1496    }
1497
1498    fn read2_abbreviated(&self, child: Child) -> (Output, Truncated) {
1499        let mut filter_paths_from_len = Vec::new();
1500        let mut add_path = |path: &Utf8Path| {
1501            let path = path.to_string();
1502            let windows = path.replace("\\", "\\\\");
1503            if windows != path {
1504                filter_paths_from_len.push(windows);
1505            }
1506            filter_paths_from_len.push(path);
1507        };
1508
1509        // List of paths that will not be measured when determining whether the output is larger
1510        // than the output truncation threshold.
1511        //
1512        // Note: avoid adding a subdirectory of an already filtered directory here, otherwise the
1513        // same slice of text will be double counted and the truncation might not happen.
1514        add_path(&self.config.src_test_suite_root);
1515        add_path(&self.config.build_test_suite_root);
1516
1517        read2_abbreviated(child, &filter_paths_from_len).expect("failed to read output")
1518    }
1519
1520    fn compose_and_run(
1521        &self,
1522        mut command: Command,
1523        lib_path: &Utf8Path,
1524        aux_path: Option<&Utf8Path>,
1525        input: Option<String>,
1526    ) -> ProcRes {
1527        let cmdline = {
1528            let cmdline = self.make_cmdline(&command, lib_path);
1529            self.logv(format_args!("executing {cmdline}"));
1530            cmdline
1531        };
1532
1533        command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::piped());
1534
1535        // Need to be sure to put both the lib_path and the aux path in the dylib
1536        // search path for the child.
1537        add_dylib_path(&mut command, iter::once(lib_path).chain(aux_path));
1538
1539        let mut child = disable_error_reporting(|| command.spawn())
1540            .unwrap_or_else(|e| panic!("failed to exec `{command:?}`: {e:?}"));
1541        if let Some(input) = input {
1542            child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
1543        }
1544
1545        let (Output { status, stdout, stderr }, truncated) = self.read2_abbreviated(child);
1546
1547        let result = ProcRes {
1548            status,
1549            stdout: String::from_utf8_lossy(&stdout).into_owned(),
1550            stderr: String::from_utf8_lossy(&stderr).into_owned(),
1551            truncated,
1552            cmdline,
1553        };
1554
1555        self.dump_output(
1556            self.config.verbose || (!result.status.success() && self.config.mode != TestMode::Ui),
1557            &command.get_program().to_string_lossy(),
1558            &result.stdout,
1559            &result.stderr,
1560        );
1561
1562        result
1563    }
1564
1565    /// Choose a compiler kind (rustc or rustdoc) for compiling test files,
1566    /// based on the test suite being tested.
1567    fn compiler_kind_for_non_aux(&self) -> CompilerKind {
1568        match self.config.suite {
1569            TestSuite::RustdocJs | TestSuite::RustdocJson | TestSuite::RustdocUi => {
1570                CompilerKind::Rustdoc
1571            }
1572
1573            // Exhaustively match all other suites.
1574            // Note that some suites never actually use this method, so the
1575            // return value for those suites is not necessarily meaningful.
1576            TestSuite::AssemblyLlvm
1577            | TestSuite::BuildStd
1578            | TestSuite::CodegenLlvm
1579            | TestSuite::CodegenUnits
1580            | TestSuite::Coverage
1581            | TestSuite::CoverageRunRustdoc
1582            | TestSuite::Crashes
1583            | TestSuite::Debuginfo
1584            | TestSuite::Incremental
1585            | TestSuite::MirOpt
1586            | TestSuite::Pretty
1587            | TestSuite::RunMake
1588            | TestSuite::RunMakeCargo
1589            | TestSuite::RustdocGui
1590            | TestSuite::RustdocHtml
1591            | TestSuite::RustdocJsStd
1592            | TestSuite::Ui
1593            | TestSuite::UiFullDeps => CompilerKind::Rustc,
1594        }
1595    }
1596
1597    fn make_compile_args(
1598        &self,
1599        compiler_kind: CompilerKind,
1600        input_file: &Utf8Path,
1601        output_file: TargetLocation,
1602        emit: Emit,
1603        allow_unused: AllowUnused,
1604        link_to_aux: LinkToAux,
1605        passes: Vec<String>, // Vec of passes under mir-opt test to be dumped
1606    ) -> Command {
1607        // FIXME(Zalathar): We should have a cleaner distinction between
1608        // `rustc` flags, `rustdoc` flags, and flags shared by both.
1609        let mut compiler = match compiler_kind {
1610            CompilerKind::Rustc => Command::new(&self.config.rustc_path),
1611            CompilerKind::Rustdoc => {
1612                Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
1613            }
1614        };
1615        compiler.arg(input_file);
1616
1617        // Enable wasm proc macros.
1618        if self.config.wasm_proc_macros {
1619            compiler.arg("-Zwasm-proc-macros");
1620        }
1621
1622        // `--disable-minification` is an unstable rustdoc option. Rustdoc UI tests intentionally
1623        // exercise diagnostics for unstable options, so don't enable them for that suite.
1624        if compiler_kind == CompilerKind::Rustdoc
1625            && self.config.disable_minification
1626            && self.config.mode != TestMode::Ui
1627        {
1628            compiler.arg("-Zunstable-options").arg("--disable-minification");
1629        }
1630
1631        // Hide libstd sources from ui tests to make sure we generate the stderr
1632        // output that users will see.
1633        // Without this, we may be producing good diagnostics in-tree but users
1634        // will not see half the information.
1635        //
1636        // This also has the benefit of more effectively normalizing output between different
1637        // compilers, so that we don't have to know the `/rustc/$sha` output to normalize after the
1638        // fact.
1639        compiler.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
1640        compiler.arg("-Ztranslate-remapped-path-to-local-path=no");
1641
1642        // Hide Cargo dependency sources from ui tests to make sure the error message doesn't
1643        // change depending on whether $CARGO_HOME is remapped or not. If this is not present,
1644        // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
1645        // source will be shown, causing a blessing hell.
1646        compiler.arg("-Z").arg(format!(
1647            "ignore-directory-in-diagnostics-source-blocks={}",
1648            home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
1649        ));
1650        // Similarly, vendored sources shouldn't be shown when running from a dist tarball.
1651        compiler.arg("-Z").arg(format!(
1652            "ignore-directory-in-diagnostics-source-blocks={}",
1653            self.config.src_root.join("vendor"),
1654        ));
1655
1656        // Optionally prevent default --sysroot if specified in test compile-flags.
1657        //
1658        // FIXME: I feel like this logic is fairly sus.
1659        if !self.props.compile_flags.iter().any(|flag| flag.starts_with("--sysroot"))
1660            && !self.config.host_rustcflags.iter().any(|flag| flag == "--sysroot")
1661        {
1662            // In stage 0, make sure we use `stage0-sysroot` instead of the bootstrap sysroot.
1663            compiler.arg("--sysroot").arg(&self.config.sysroot_base);
1664        }
1665
1666        // If the provided codegen backend is not LLVM, we need to pass it.
1667        if let Some(ref backend) = self.config.override_codegen_backend {
1668            compiler.arg(format!("-Zcodegen-backend={}", backend));
1669        }
1670
1671        // Optionally prevent default --target if specified in test compile-flags.
1672        let custom_target = self.props.compile_flags.iter().any(|x| x.starts_with("--target"));
1673
1674        if !custom_target {
1675            let target =
1676                if self.props.force_host { &*self.config.host } else { &*self.config.target };
1677
1678            compiler.arg(&format!("--target={}", target));
1679            if target.ends_with(".json") {
1680                // `-Zunstable-options` is necessary when compiletest is running with custom targets
1681                // (such as synthetic targets used to bless mir-opt tests).
1682                compiler.arg("-Zunstable-options");
1683            }
1684        }
1685        self.set_revision_flags(&mut compiler);
1686
1687        if compiler_kind == CompilerKind::Rustc {
1688            if let Some(ref incremental_dir) = self.props.incremental_dir {
1689                compiler.args(&["-C", &format!("incremental={}", incremental_dir)]);
1690                compiler.args(&["-Z", "incremental-verify-ich"]);
1691            }
1692
1693            if self.config.mode == TestMode::CodegenUnits {
1694                compiler.args(&["-Z", "human_readable_cgu_names"]);
1695            }
1696
1697            if self.config.mode == TestMode::DebugInfo && cfg!(target_os = "windows") {
1698                // Prevent debugger processes from creating new console windows.
1699                compiler.args(&["-Z", r#"crate-attr=windows_subsystem="windows""#]);
1700            }
1701        }
1702
1703        if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc {
1704            match self.config.mode {
1705                TestMode::Ui => {
1706                    // If optimize-tests is true we still only want to optimize tests that actually get
1707                    // executed and that don't specify their own optimization levels.
1708                    // Note: aux libs don't have a pass/fail mode, so they won't get optimized
1709                    // unless compile-flags are set in the aux file.
1710                    // FIXME(Zalathar): We could also optimize run-fail/run-crash tests,
1711                    // but it's unclear whether that would be helpful or a waste of time.
1712                    if self.effective_pass_fail_mode() == Some(PassFailMode::RunPass)
1713                        && !self
1714                            .props
1715                            .compile_flags
1716                            .iter()
1717                            .any(|arg| arg == "-O" || arg.contains("opt-level"))
1718                    {
1719                        compiler.arg("-O");
1720                    }
1721                }
1722                TestMode::DebugInfo => { /* debuginfo tests must be unoptimized */ }
1723                TestMode::CoverageMap | TestMode::CoverageRun => {
1724                    // Coverage mappings and coverage reports are affected by
1725                    // optimization level, so they ignore the optimize-tests
1726                    // setting and set an optimization level in their mode's
1727                    // compile flags (below) or in per-test `compile-flags`.
1728                }
1729                _ => {
1730                    compiler.arg("-O");
1731                }
1732            }
1733        }
1734
1735        let set_mir_dump_dir = |rustc: &mut Command| {
1736            let mir_dump_dir = self.output_base_dir();
1737            let mut dir_opt = "-Zdump-mir-dir=".to_string();
1738            dir_opt.push_str(mir_dump_dir.as_str());
1739            debug!("dir_opt: {:?}", dir_opt);
1740            rustc.arg(dir_opt);
1741        };
1742
1743        match self.config.mode {
1744            TestMode::Incremental => {
1745                // If we are extracting and matching errors in the new
1746                // fashion, then you want JSON mode. Old-skool error
1747                // patterns still match the raw compiler output.
1748                if self.props.error_patterns.is_empty()
1749                    && self.props.regex_error_patterns.is_empty()
1750                {
1751                    compiler.args(&["--error-format", "json"]);
1752                    compiler.args(&["--json", "future-incompat"]);
1753                }
1754                compiler.arg("-Zui-testing");
1755                compiler.arg("-Zdeduplicate-diagnostics=no");
1756            }
1757            TestMode::Ui => {
1758                if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
1759                    compiler.args(&["--error-format", "json"]);
1760                    compiler.args(&["--json", "future-incompat"]);
1761                }
1762                compiler.arg("-Ccodegen-units=1");
1763                // Hide line numbers to reduce churn
1764                compiler.arg("-Zui-testing");
1765                compiler.arg("-Zdeduplicate-diagnostics=no");
1766                compiler.arg("-Zwrite-long-types-to-disk=no");
1767                // FIXME: use this for other modes too, for perf?
1768                compiler.arg("-Cstrip=debuginfo");
1769
1770                if self.config.parallel_frontend_enabled() {
1771                    // Currently, we only use multiple threads for the UI test suite,
1772                    // because UI tests can effectively verify the parallel frontend and
1773                    // require minimal modification. The option will later be extended to
1774                    // other test suites.
1775                    compiler.arg(&format!("-Zthreads={}", self.config.parallel_frontend_threads));
1776                }
1777            }
1778            TestMode::MirOpt => {
1779                // We check passes under test to minimize the mir-opt test dump
1780                // if files_for_miropt_test parses the passes, we dump only those passes
1781                // otherwise we conservatively pass -Zdump-mir=all
1782                let zdump_arg = if !passes.is_empty() {
1783                    format!("-Zdump-mir={}", passes.join(" | "))
1784                } else {
1785                    "-Zdump-mir=all".to_string()
1786                };
1787
1788                compiler.args(&[
1789                    "-Copt-level=1",
1790                    &zdump_arg,
1791                    "-Zvalidate-mir",
1792                    "-Zlint-mir",
1793                    "-Zdump-mir-exclude-pass-number",
1794                    "-Zmir-include-spans=false", // remove span comments from NLL MIR dumps
1795                    "--crate-type=rlib",
1796                ]);
1797                if let Some(pass) = &self.props.mir_unit_test {
1798                    compiler
1799                        .args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
1800                } else {
1801                    compiler.args(&[
1802                        "-Zmir-opt-level=4",
1803                        "-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals",
1804                    ]);
1805                }
1806
1807                set_mir_dump_dir(&mut compiler);
1808            }
1809            TestMode::CoverageMap => {
1810                compiler.arg("-Cinstrument-coverage");
1811                // These tests only compile to LLVM IR, so they don't need the
1812                // profiler runtime to be present.
1813                compiler.arg("-Zno-profiler-runtime");
1814                // Coverage mappings are sensitive to MIR optimizations, and
1815                // the current snapshots assume `opt-level=2` unless overridden
1816                // by `compile-flags`.
1817                compiler.arg("-Copt-level=2");
1818            }
1819            TestMode::CoverageRun => {
1820                compiler.arg("-Cinstrument-coverage");
1821                // Coverage reports are sometimes sensitive to optimizations,
1822                // and the current snapshots assume `opt-level=2` unless
1823                // overridden by `compile-flags`.
1824                compiler.arg("-Copt-level=2");
1825            }
1826            TestMode::Assembly | TestMode::Codegen => {
1827                compiler.arg("-Cdebug-assertions=no");
1828                // For assembly and codegen tests, we want to use the same order
1829                // of the items of a codegen unit as the source order, so that
1830                // we can compare the output with the source code through filecheck.
1831                compiler.arg("-Zcodegen-source-order");
1832            }
1833            TestMode::Crashes => {
1834                set_mir_dump_dir(&mut compiler);
1835            }
1836            TestMode::CodegenUnits => {
1837                compiler.arg("-Zprint-mono-items");
1838            }
1839            TestMode::Pretty
1840            | TestMode::DebugInfo
1841            | TestMode::RustdocHtml
1842            | TestMode::RustdocJson
1843            | TestMode::RunMake
1844            | TestMode::RustdocJs => {
1845                // do not use JSON output
1846            }
1847        }
1848
1849        if self.props.remap_src_base {
1850            compiler.arg(format!(
1851                "--remap-path-prefix={}={}",
1852                self.config.src_test_suite_root, FAKE_SRC_BASE,
1853            ));
1854        }
1855
1856        if compiler_kind == CompilerKind::Rustc {
1857            match emit {
1858                Emit::None => {}
1859                Emit::Metadata => {
1860                    compiler.args(&["--emit", "metadata"]);
1861                }
1862                Emit::LlvmIr => {
1863                    compiler.args(&["--emit", "llvm-ir"]);
1864                }
1865                Emit::Mir => {
1866                    compiler.args(&["--emit", "mir"]);
1867                }
1868                Emit::Asm => {
1869                    compiler.args(&["--emit", "asm"]);
1870                }
1871                Emit::LinkArgsAsm => {
1872                    compiler.args(&["-Clink-args=--emit=asm"]);
1873                }
1874            }
1875        }
1876
1877        if compiler_kind == CompilerKind::Rustc {
1878            if self.config.target == "wasm32-unknown-unknown" || self.is_vxworks_pure_static() {
1879                // rustc.arg("-g"); // get any backtrace at all on errors
1880            } else if !self.props.no_prefer_dynamic {
1881                compiler.args(&["-C", "prefer-dynamic"]);
1882            }
1883        }
1884
1885        match output_file {
1886            // If the test's compile flags specify an output path with `-o`,
1887            // avoid a compiler warning about `--out-dir` being ignored.
1888            _ if self.props.compile_flags.iter().any(|flag| flag == "-o") => {}
1889            TargetLocation::ThisFile(path) => {
1890                compiler.arg("-o").arg(path);
1891            }
1892            TargetLocation::ThisDirectory(path) => match compiler_kind {
1893                CompilerKind::Rustdoc => {
1894                    // `rustdoc` uses `-o` for the output directory.
1895                    compiler.arg("-o").arg(path);
1896                }
1897                CompilerKind::Rustc => {
1898                    compiler.arg("--out-dir").arg(path);
1899                }
1900            },
1901        }
1902
1903        // FIXME(#160895): While the new solver is enabled by default on nightly,
1904        // we don't want to use it in our tests for now.
1905        compiler.args(["-Znext-solver=coherence"]);
1906
1907        match self.config.compare_mode {
1908            Some(CompareMode::Polonius) => {
1909                compiler.args(&["-Zpolonius=next"]);
1910            }
1911            Some(CompareMode::NextSolver) => {
1912                compiler.args(&["-Znext-solver"]);
1913            }
1914            Some(CompareMode::NextSolverCoherence) => {
1915                compiler.args(&["-Znext-solver=coherence"]);
1916            }
1917            Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => {
1918                compiler.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
1919            }
1920            Some(CompareMode::SplitDwarf) => {
1921                compiler.args(&["-Csplit-debuginfo=unpacked"]);
1922            }
1923            Some(CompareMode::SplitDwarfSingle) => {
1924                compiler.args(&["-Csplit-debuginfo=packed"]);
1925            }
1926            None => {}
1927        }
1928
1929        // Add `-A unused` before `config` flags and in-test (`props`) flags, so that they can
1930        // overwrite this.
1931        // Don't allow `unused_attributes` since these are usually actual mistakes, rather than just unused code.
1932        if let AllowUnused::Yes = allow_unused {
1933            compiler.args(&["-A", "unused", "-W", "unused_attributes"]);
1934        }
1935
1936        // Allow tests to use internal and incomplete features.
1937        compiler.args(&["-A", "internal_features"]);
1938        compiler.args(&["-A", "incomplete_features"]);
1939
1940        // Allow tests to have unused parens and braces.
1941        // Add #![deny(unused_parens, unused_braces)] to the test file if you want to
1942        // test that these lints are working.
1943        compiler.args(&["-A", "unused_parens"]);
1944        compiler.args(&["-A", "unused_braces"]);
1945
1946        if self.props.force_host {
1947            self.maybe_add_external_args(&mut compiler, &self.config.host_rustcflags);
1948            if compiler_kind == CompilerKind::Rustc
1949                && let Some(ref linker) = self.config.host_linker
1950            {
1951                compiler.arg(format!("-Clinker={linker}"));
1952            }
1953        } else {
1954            self.maybe_add_external_args(&mut compiler, &self.config.target_rustcflags);
1955            if compiler_kind == CompilerKind::Rustc
1956                && let Some(ref linker) = self.config.target_linker
1957            {
1958                compiler.arg(format!("-Clinker={linker}"));
1959            }
1960        }
1961
1962        // Use dynamic musl for tests because static doesn't allow creating dylibs
1963        if self.config.host.contains("musl") || self.is_vxworks_pure_dynamic() {
1964            compiler.arg("-Ctarget-feature=-crt-static");
1965        }
1966
1967        if let LinkToAux::Yes = link_to_aux {
1968            // if we pass an `-L` argument to a directory that doesn't exist,
1969            // macOS ld emits warnings which disrupt the .stderr files
1970            if self.has_aux_dir() {
1971                compiler.arg("-L").arg(self.aux_output_dir_name());
1972            }
1973        }
1974
1975        // FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with
1976        // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`.
1977        //
1978        // We could apply these last and override any provided flags. That would ensure that the
1979        // build works, but some tests want to exercise that mixing panic modes in specific ways is
1980        // rejected. So we enable aborting panics and unwind tables before adding flags, just to
1981        // change the default.
1982        //
1983        // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics.
1984        if self.props.add_minicore {
1985            compiler.arg("-Cpanic=abort");
1986            compiler.arg("-Cforce-unwind-tables=yes");
1987        }
1988
1989        compiler.args(&self.props.compile_flags);
1990
1991        compiler
1992    }
1993
1994    fn make_exe_name(&self) -> Utf8PathBuf {
1995        // Using a single letter here to keep the path length down for
1996        // Windows.  Some test names get very long.  rustc creates `rcgu`
1997        // files with the module name appended to it which can more than
1998        // double the length.
1999        let mut f = self.output_base_dir().join("a");
2000        // FIXME: This is using the host architecture exe suffix, not target!
2001        if self.config.target.contains("emscripten") {
2002            f = f.with_extra_extension("js");
2003        } else if self.config.target.starts_with("wasm") {
2004            f = f.with_extra_extension("wasm");
2005        } else if self.config.target.contains("spirv") {
2006            f = f.with_extra_extension("spv");
2007        } else if !env::consts::EXE_SUFFIX.is_empty() {
2008            f = f.with_extra_extension(env::consts::EXE_SUFFIX);
2009        }
2010        f
2011    }
2012
2013    fn make_run_args(&self) -> ProcArgs {
2014        // If we've got another tool to run under (valgrind),
2015        // then split apart its command
2016        let mut args = self.split_maybe_args(&self.config.runner);
2017
2018        let exe_file = self.make_exe_name();
2019
2020        args.push(exe_file.into_os_string());
2021
2022        // Add the arguments in the run_flags directive
2023        args.extend(self.props.run_flags.iter().map(OsString::from));
2024
2025        let prog = args.remove(0);
2026        ProcArgs { prog, args }
2027    }
2028
2029    fn split_maybe_args(&self, argstr: &Option<String>) -> Vec<OsString> {
2030        match *argstr {
2031            Some(ref s) => s
2032                .split(' ')
2033                .filter_map(|s| {
2034                    if s.chars().all(|c| c.is_whitespace()) {
2035                        None
2036                    } else {
2037                        Some(OsString::from(s))
2038                    }
2039                })
2040                .collect(),
2041            None => Vec::new(),
2042        }
2043    }
2044
2045    fn make_cmdline(&self, command: &Command, libpath: &Utf8Path) -> String {
2046        use crate::util;
2047
2048        // Linux and mac don't require adjusting the library search path
2049        if cfg!(unix) {
2050            format!("{:?}", command)
2051        } else {
2052            // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
2053            // for diagnostic purposes
2054            fn lib_path_cmd_prefix(path: &str) -> String {
2055                format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
2056            }
2057
2058            format!("{} {:?}", lib_path_cmd_prefix(libpath.as_str()), command)
2059        }
2060    }
2061
2062    fn dump_output(&self, print_output: bool, proc_name: &str, out: &str, err: &str) {
2063        let revision =
2064            if let Some(r) = self.variant.revision() { format!("{}.", r) } else { String::new() };
2065
2066        self.dump_output_file(out, &format!("{}out", revision));
2067        self.dump_output_file(err, &format!("{}err", revision));
2068
2069        if !print_output {
2070            return;
2071        }
2072
2073        let path = Utf8Path::new(proc_name);
2074        let proc_name = if path.file_stem().is_some_and(|p| p == "rmake") {
2075            String::from_iter(
2076                path.parent()
2077                    .unwrap()
2078                    .file_name()
2079                    .into_iter()
2080                    .chain(Some("/"))
2081                    .chain(path.file_name()),
2082            )
2083        } else {
2084            path.file_name().unwrap().into()
2085        };
2086        writeln!(self.stdout, "------{proc_name} stdout------------------------------");
2087        writeln!(self.stdout, "{}", out);
2088        writeln!(self.stdout, "------{proc_name} stderr------------------------------");
2089        writeln!(self.stdout, "{}", err);
2090        writeln!(self.stdout, "------------------------------------------");
2091    }
2092
2093    fn dump_output_file(&self, out: &str, extension: &str) {
2094        let outfile = self.make_out_name(extension);
2095        fs::write(outfile.as_std_path(), out)
2096            .unwrap_or_else(|err| panic!("failed to write {outfile}: {err:?}"));
2097    }
2098
2099    /// Creates a filename for output with the given extension.
2100    /// E.g., `/.../testname.revision.mode/testname.extension`.
2101    fn make_out_name(&self, extension: &str) -> Utf8PathBuf {
2102        self.output_base_name().with_extension(extension)
2103    }
2104
2105    /// Gets the directory where auxiliary files are written.
2106    /// E.g., `/.../testname.revision.mode/auxiliary/`.
2107    fn aux_output_dir_name(&self) -> Utf8PathBuf {
2108        self.output_base_dir()
2109            .join("auxiliary")
2110            .with_extra_extension(self.config.mode.aux_dir_disambiguator())
2111    }
2112
2113    /// Gets the directory where auxiliary binaries are written.
2114    /// E.g., `/.../testname.revision.mode/auxiliary/bin`.
2115    fn aux_bin_output_dir_name(&self) -> Utf8PathBuf {
2116        self.aux_output_dir_name().join("bin")
2117    }
2118
2119    /// The revision, ignored for incremental compilation since it wants all revisions in
2120    /// the same directory.
2121    fn variant_with_safe_revision(&self) -> TestVariant {
2122        if self.config.mode == TestMode::Incremental {
2123            TestVariant { revision: None, debugger: self.variant.debugger }
2124        } else {
2125            self.variant.clone()
2126        }
2127    }
2128
2129    /// Gets the absolute path to the directory where all output for the given
2130    /// test/revision should reside.
2131    /// E.g., `/path/to/build/host-tuple/test/ui/relative/testname.revision.mode/`.
2132    fn output_base_dir(&self) -> Utf8PathBuf {
2133        output_base_dir(self.config, self.testpaths, &self.variant_with_safe_revision())
2134    }
2135
2136    /// Gets the absolute path to the base filename used as output for the given
2137    /// test/revision.
2138    /// E.g., `/.../relative/testname.revision.mode/testname`.
2139    fn output_base_name(&self) -> Utf8PathBuf {
2140        output_base_name(self.config, self.testpaths, &self.variant_with_safe_revision())
2141    }
2142
2143    /// Prints a message to (captured) stdout if `config.verbose` is true.
2144    /// The message is also logged to `tracing::debug!` regardless of verbosity.
2145    ///
2146    /// Use `format_args!` as the argument to perform formatting if required.
2147    fn logv(&self, message: impl fmt::Display) {
2148        debug!("{message}");
2149        if self.config.verbose {
2150            // Note: `./x test ... --verbose --no-capture` is needed to see this print.
2151            writeln!(self.stdout, "{message}");
2152        }
2153    }
2154
2155    /// Prefix to print before error messages. Normally just `error`, but also
2156    /// includes the revision name for tests that use revisions.
2157    #[must_use]
2158    fn error_prefix(&self) -> String {
2159        match self.variant.revision() {
2160            Some(rev) => format!("error in revision `{rev}`"),
2161            None => format!("error"),
2162        }
2163    }
2164
2165    #[track_caller]
2166    fn fatal(&self, err: &str) -> ! {
2167        writeln!(self.stdout, "\n{prefix}: {err}", prefix = self.error_prefix());
2168        error!("fatal error, panic: {:?}", err);
2169        panic!("fatal error");
2170    }
2171
2172    fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
2173        self.fatal_proc_rec_general(err, None, proc_res, || ());
2174    }
2175
2176    /// Underlying implementation of [`Self::fatal_proc_rec`], providing some
2177    /// extra capabilities not needed by most callers.
2178    fn fatal_proc_rec_general(
2179        &self,
2180        err: &str,
2181        extra_note: Option<&str>,
2182        proc_res: &ProcRes,
2183        callback_before_unwind: impl FnOnce(),
2184    ) -> ! {
2185        writeln!(self.stdout, "\n{prefix}: {err}", prefix = self.error_prefix());
2186
2187        // Some callers want to print additional notes after the main error message.
2188        if let Some(note) = extra_note {
2189            writeln!(self.stdout, "{note}");
2190        }
2191
2192        // Print the details and output of the subprocess that caused this test to fail.
2193        writeln!(self.stdout, "{}", proc_res.format_info());
2194
2195        // Some callers want print more context or show a custom diff before the unwind occurs.
2196        callback_before_unwind();
2197
2198        // Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
2199        // compiletest, which is unnecessary noise.
2200        std::panic::resume_unwind(Box::new(()));
2201    }
2202
2203    // codegen tests (using FileCheck)
2204
2205    fn compile_test_and_save_ir(&self) -> (ProcRes, Utf8PathBuf) {
2206        let output_path = self.output_base_name().with_extension("ll");
2207        let input_file = &self.testpaths.file;
2208        let rustc = self.make_compile_args(
2209            CompilerKind::Rustc,
2210            input_file,
2211            TargetLocation::ThisFile(output_path.clone()),
2212            Emit::LlvmIr,
2213            AllowUnused::No,
2214            LinkToAux::Yes,
2215            Vec::new(),
2216        );
2217
2218        let proc_res = self.compose_and_run_compiler(rustc, None);
2219        (proc_res, output_path)
2220    }
2221
2222    fn verify_with_filecheck(&self, output: &Utf8Path) -> ProcRes {
2223        let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
2224        filecheck.arg("--input-file").arg(output).arg(&self.testpaths.file);
2225
2226        // Because we use custom prefixes, we also have to register the default prefix.
2227        filecheck.arg("--check-prefix=CHECK");
2228
2229        // FIXME(#134510): auto-registering revision names as check prefix is a bit sketchy, and
2230        // that having to pass `--allow-unused-prefix` is an unfortunate side-effect of not knowing
2231        // whether the test author actually wanted revision-specific check prefixes or not.
2232        //
2233        // TL;DR We may not want to conflate `compiletest` revisions and `FileCheck` prefixes.
2234
2235        // HACK: tests are allowed to use a revision name as a check prefix.
2236        if let Some(rev) = self.variant.revision() {
2237            filecheck.arg("--check-prefix").arg(rev);
2238        }
2239
2240        // HACK: the filecheck tool normally fails if a prefix is defined but not used. However,
2241        // sometimes revisions are used to specify *compiletest* directives which are not FileCheck
2242        // concerns.
2243        filecheck.arg("--allow-unused-prefixes");
2244
2245        // Provide more context on failures.
2246        filecheck.args(&["--dump-input-context", "100"]);
2247
2248        // Add custom flags supplied by the `filecheck-flags:` test directive.
2249        filecheck.args(&self.props.filecheck_flags);
2250
2251        // FIXME(jieyouxu): don't pass an empty Path
2252        self.compose_and_run(filecheck, Utf8Path::new(""), None, None)
2253    }
2254
2255    fn charset() -> &'static str {
2256        // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
2257        if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" }
2258    }
2259
2260    fn get_lines(&self, path: &Utf8Path, mut other_files: Option<&mut Vec<String>>) -> Vec<usize> {
2261        let content = fs::read_to_string(path.as_std_path()).unwrap();
2262        let mut ignore = false;
2263        content
2264            .lines()
2265            .enumerate()
2266            .filter_map(|(line_nb, line)| {
2267                if (line.trim_start().starts_with("pub mod ")
2268                    || line.trim_start().starts_with("mod "))
2269                    && line.ends_with(';')
2270                {
2271                    if let Some(ref mut other_files) = other_files {
2272                        other_files.push(line.rsplit("mod ").next().unwrap().replace(';', ""));
2273                    }
2274                    None
2275                } else {
2276                    let sline = line.rsplit("///").next().unwrap();
2277                    let line = sline.trim_start();
2278                    if line.starts_with("```") {
2279                        if ignore {
2280                            ignore = false;
2281                            None
2282                        } else {
2283                            ignore = true;
2284                            Some(line_nb + 1)
2285                        }
2286                    } else {
2287                        None
2288                    }
2289                }
2290            })
2291            .collect()
2292    }
2293
2294    /// This method is used for `//@ check-test-line-numbers-match`.
2295    ///
2296    /// It checks that doctests line in the displayed doctest "name" matches where they are
2297    /// defined in source code.
2298    fn check_rustdoc_test_option(&self, res: ProcRes) {
2299        let mut other_files = Vec::new();
2300        let mut files: HashMap<String, Vec<usize>> = HashMap::new();
2301        let normalized = fs::canonicalize(&self.testpaths.file).expect("failed to canonicalize");
2302        let normalized = normalized.to_str().unwrap().replace('\\', "/");
2303        files.insert(normalized, self.get_lines(&self.testpaths.file, Some(&mut other_files)));
2304        for other_file in other_files {
2305            let mut path = self.testpaths.file.clone();
2306            path.set_file_name(&format!("{}.rs", other_file));
2307            let path = path.canonicalize_utf8().expect("failed to canonicalize");
2308            let normalized = path.as_str().replace('\\', "/");
2309            files.insert(normalized, self.get_lines(&path, None));
2310        }
2311
2312        let mut tested = 0;
2313        for _ in res.stdout.split('\n').filter(|s| s.starts_with("test ")).inspect(|s| {
2314            if let Some((left, right)) = s.split_once(" - ") {
2315                let path = left.rsplit("test ").next().unwrap();
2316                let path = fs::canonicalize(&path).expect("failed to canonicalize");
2317                let path = path.to_str().unwrap().replace('\\', "/");
2318                if let Some(ref mut v) = files.get_mut(&path) {
2319                    tested += 1;
2320                    let mut iter = right.split("(line ");
2321                    iter.next();
2322                    let line = iter
2323                        .next()
2324                        .unwrap_or(")")
2325                        .split(')')
2326                        .next()
2327                        .unwrap_or("0")
2328                        .parse()
2329                        .unwrap_or(0);
2330                    if let Ok(pos) = v.binary_search(&line) {
2331                        v.remove(pos);
2332                    } else {
2333                        self.fatal_proc_rec(
2334                            &format!("Not found doc test: \"{}\" in \"{}\":{:?}", s, path, v),
2335                            &res,
2336                        );
2337                    }
2338                }
2339            }
2340        }) {}
2341        if tested == 0 {
2342            self.fatal_proc_rec(&format!("No test has been found... {:?}", files), &res);
2343        } else {
2344            for (entry, v) in &files {
2345                if !v.is_empty() {
2346                    self.fatal_proc_rec(
2347                        &format!(
2348                            "Not found test at line{} \"{}\":{:?}",
2349                            if v.len() > 1 { "s" } else { "" },
2350                            entry,
2351                            v
2352                        ),
2353                        &res,
2354                    );
2355                }
2356            }
2357        }
2358    }
2359
2360    fn force_color_svg(&self) -> bool {
2361        self.props.compile_flags.iter().any(|s| s.contains("--color=always"))
2362    }
2363
2364    /// Returns the lines for the by-lines comparison, normalized for the
2365    /// parallel front-end: for SVG output, strip the header line and `y`
2366    /// offsets; otherwise, filter out padded empty code lines (a single `|`).
2367    fn lines_for_comparison(&self, output: &str) -> Vec<String> {
2368        if self.force_color_svg() {
2369            let strip_y = static_regex!(r#"y="\d+px""#);
2370            output
2371                .lines()
2372                // anstyle_svg causes environment-dependent width parameter
2373                .skip(1)
2374                .map(|line| strip_y.replace_all(line, r#"y="0px""#).into_owned())
2375                .collect()
2376        } else {
2377            output.lines().filter(|l| l.trim() != "|").map(str::to_owned).collect()
2378        }
2379    }
2380
2381    fn load_compare_outputs(
2382        &self,
2383        proc_res: &ProcRes,
2384        output_kind: TestOutput,
2385        explicit_format: bool,
2386    ) -> usize {
2387        let stderr_bits = format!("{}bit.stderr", self.config.get_pointer_width());
2388        let (stderr_kind, stdout_kind) = match output_kind {
2389            TestOutput::Compile => (
2390                if self.force_color_svg() {
2391                    if self.config.target.contains("windows") {
2392                        // We single out Windows here because some of the CLI coloring is
2393                        // specifically changed for Windows.
2394                        UI_WINDOWS_SVG
2395                    } else {
2396                        UI_SVG
2397                    }
2398                } else if self.props.stderr_per_bitwidth {
2399                    &stderr_bits
2400                } else {
2401                    UI_STDERR
2402                },
2403                UI_STDOUT,
2404            ),
2405            TestOutput::Run => (UI_RUN_STDERR, UI_RUN_STDOUT),
2406        };
2407
2408        let expected_stderr = self.load_expected_output(stderr_kind);
2409        let expected_stdout = self.load_expected_output(stdout_kind);
2410
2411        let mut normalized_stdout =
2412            self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout);
2413        match output_kind {
2414            TestOutput::Run if self.config.remote_test_client.is_some() => {
2415                // When tests are run using the remote-test-client, the string
2416                // 'uploaded "$TEST_BUILD_DIR/<test_executable>, waiting for result"'
2417                // is printed to stdout by the client and then captured in the ProcRes,
2418                // so it needs to be removed when comparing the run-pass test execution output.
2419                normalized_stdout = static_regex!(
2420                    "^uploaded \"\\$TEST_BUILD_DIR(/[[:alnum:]_\\-.]+)+\", waiting for result\n"
2421                )
2422                .replace(&normalized_stdout, "")
2423                .to_string();
2424                // When there is a panic, the remote-test-client also prints "died due to signal";
2425                // that needs to be removed as well.
2426                normalized_stdout = static_regex!("^died due to signal [0-9]+\n")
2427                    .replace(&normalized_stdout, "")
2428                    .to_string();
2429                // FIXME: it would be much nicer if we could just tell the remote-test-client to not
2430                // print these things.
2431            }
2432            _ => {}
2433        };
2434
2435        let stderr;
2436        let normalized_stderr;
2437
2438        if self.force_color_svg() {
2439            let normalized = self.normalize_output(&proc_res.stderr, &self.props.normalize_stderr);
2440            stderr = anstyle_svg::Term::new().render_svg(&normalized);
2441            normalized_stderr = stderr.clone();
2442        } else {
2443            stderr = if explicit_format {
2444                proc_res.stderr.clone()
2445            } else {
2446                json::extract_rendered(&proc_res.stderr)
2447            };
2448            normalized_stderr = self.normalize_output(&stderr, &self.props.normalize_stderr);
2449        }
2450
2451        let mut errors = 0;
2452        match output_kind {
2453            TestOutput::Compile => {
2454                if !self.props.dont_check_compiler_stdout {
2455                    if self
2456                        .compare_output(
2457                            stdout_kind,
2458                            &normalized_stdout,
2459                            &proc_res.stdout,
2460                            &expected_stdout,
2461                        )
2462                        .should_error()
2463                    {
2464                        errors += 1;
2465                    }
2466                }
2467                if !self.props.dont_check_compiler_stderr {
2468                    if self
2469                        .compare_output(stderr_kind, &normalized_stderr, &stderr, &expected_stderr)
2470                        .should_error()
2471                    {
2472                        errors += 1;
2473                    }
2474                }
2475            }
2476            TestOutput::Run => {
2477                if self
2478                    .compare_output(
2479                        stdout_kind,
2480                        &normalized_stdout,
2481                        &proc_res.stdout,
2482                        &expected_stdout,
2483                    )
2484                    .should_error()
2485                {
2486                    errors += 1;
2487                }
2488
2489                if self
2490                    .compare_output(stderr_kind, &normalized_stderr, &stderr, &expected_stderr)
2491                    .should_error()
2492                {
2493                    errors += 1;
2494                }
2495            }
2496        }
2497        errors
2498    }
2499
2500    fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
2501        // Crude heuristic to detect when the output should have JSON-specific
2502        // normalization steps applied.
2503        let rflags = self.props.run_flags.join(" ");
2504        let cflags = self.props.compile_flags.join(" ");
2505        let json = rflags.contains("--format json")
2506            || rflags.contains("--format=json")
2507            || cflags.contains("--error-format json")
2508            || cflags.contains("--error-format pretty-json")
2509            || cflags.contains("--error-format=json")
2510            || cflags.contains("--error-format=pretty-json")
2511            || cflags.contains("--output-format json")
2512            || cflags.contains("--output-format=json");
2513
2514        let mut normalized = output.to_string();
2515
2516        let mut normalize_path = |from: &Utf8Path, to: &str| {
2517            let from = if json { &from.as_str().replace("\\", "\\\\") } else { from.as_str() };
2518
2519            normalized = normalized.replace(from, to);
2520        };
2521
2522        let parent_dir = self.testpaths.file.parent().unwrap();
2523        normalize_path(parent_dir, "$DIR");
2524
2525        if self.props.remap_src_base {
2526            let mut remapped_parent_dir = Utf8PathBuf::from(FAKE_SRC_BASE);
2527            if self.testpaths.relative_dir != Utf8Path::new("") {
2528                remapped_parent_dir.push(&self.testpaths.relative_dir);
2529            }
2530            normalize_path(&remapped_parent_dir, "$DIR");
2531        }
2532
2533        let base_dir = Utf8Path::new("/rustc/FAKE_PREFIX");
2534        // Fake paths into the libstd/libcore
2535        normalize_path(&base_dir.join("library"), "$SRC_DIR");
2536        // `ui-fulldeps` tests can show paths to the compiler source when testing macros from
2537        // `rustc_macros`
2538        // eg. /home/user/rust/compiler
2539        normalize_path(&base_dir.join("compiler"), "$COMPILER_DIR");
2540
2541        // Real paths into the libstd/libcore
2542        let rust_src_dir = &self.config.sysroot_base.join("lib/rustlib/src/rust");
2543        rust_src_dir.try_exists().expect(&*format!("{} should exists", rust_src_dir));
2544        let rust_src_dir =
2545            rust_src_dir.read_link_utf8().unwrap_or_else(|_| rust_src_dir.to_path_buf());
2546        normalize_path(&rust_src_dir.join("library"), "$SRC_DIR_REAL");
2547
2548        // Real paths into the compiler
2549        let rustc_src_dir = &self.config.sysroot_base.join("lib/rustlib/rustc-src/rust");
2550        rustc_src_dir.try_exists().expect(&*format!("{} should exists", rustc_src_dir));
2551        let rustc_src_dir = rustc_src_dir.read_link_utf8().unwrap_or(rustc_src_dir.to_path_buf());
2552        normalize_path(&rustc_src_dir.join("compiler"), "$COMPILER_DIR_REAL");
2553
2554        // eg.
2555        // /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui/<test_dir>/$name.$revision.$mode/
2556        normalize_path(&self.output_base_dir(), "$TEST_BUILD_DIR");
2557        // Same as above, but with a canonicalized path.
2558        // This is required because some tests print canonical paths inside test build directory,
2559        // so if the build directory is a symlink, normalization doesn't help.
2560        //
2561        // NOTE: There are also tests which print the non-canonical name, so we need both this and
2562        // the above normalizations.
2563        normalize_path(&self.output_base_dir().canonicalize_utf8().unwrap(), "$TEST_BUILD_DIR");
2564        // eg. /home/user/rust/build
2565        normalize_path(&self.config.build_root, "$BUILD_DIR");
2566
2567        if json {
2568            // escaped newlines in json strings should be readable
2569            // in the stderr files. There's no point in being correct,
2570            // since only humans process the stderr files.
2571            // Thus we just turn escaped newlines back into newlines.
2572            normalized = normalized.replace("\\n", "\n");
2573        }
2574
2575        // If there are `$SRC_DIR` normalizations with line and column numbers, then replace them
2576        // with placeholders as we do not want tests needing updated when compiler source code
2577        // changes.
2578        // eg. $SRC_DIR/libcore/mem.rs:323:14 becomes $SRC_DIR/libcore/mem.rs:LL:COL
2579        normalized = static_regex!("SRC_DIR(.+):\\d+:\\d+(: \\d+:\\d+)?")
2580            .replace_all(&normalized, "SRC_DIR$1:LL:COL")
2581            .into_owned();
2582
2583        normalized = Self::normalize_platform_differences(&normalized);
2584
2585        // Normalize long type name hash.
2586        normalized =
2587            static_regex!(r"\$TEST_BUILD_DIR/(?P<filename>[^\.]+).long-type-(?P<hash>\d+).txt")
2588                .replace_all(&normalized, |caps: &Captures<'_>| {
2589                    format!(
2590                        "$TEST_BUILD_DIR/{filename}.long-type-$LONG_TYPE_HASH.txt",
2591                        filename = &caps["filename"]
2592                    )
2593                })
2594                .into_owned();
2595
2596        // Normalize thread IDs in panic messages
2597        normalized = static_regex!(r"thread '(?P<name>.*?)' \((rtid )?\d+\) panicked")
2598            .replace_all(&normalized, "thread '$name' ($$TID) panicked")
2599            .into_owned();
2600
2601        normalized = normalized.replace("\t", "\\t"); // makes tabs visible
2602
2603        // Remove test annotations like `//~ ERROR text` from the output,
2604        // since they duplicate actual errors and make the output hard to read.
2605        // This mirrors the regex in src/tools/tidy/src/style.rs, please update
2606        // both if either are changed.
2607        normalized =
2608            static_regex!("\\s*//(\\[.*\\])?~.*").replace_all(&normalized, "").into_owned();
2609
2610        // This code normalizes various hashes in v0 symbol mangling that is
2611        // emitted in the ui and mir-opt tests.
2612        let v0_crate_hash_prefix_re = static_regex!(r"_R.*?Cs[0-9a-zA-Z]+_");
2613        let v0_crate_hash_re = static_regex!(r"Cs[0-9a-zA-Z]+_");
2614
2615        const V0_CRATE_HASH_PLACEHOLDER: &str = r"CsCRATE_HASH_";
2616        if v0_crate_hash_prefix_re.is_match(&normalized) {
2617            // Normalize crate hash
2618            normalized =
2619                v0_crate_hash_re.replace_all(&normalized, V0_CRATE_HASH_PLACEHOLDER).into_owned();
2620        }
2621
2622        let v0_back_ref_prefix_re = static_regex!(r"\(_R.*?B[0-9a-zA-Z]_");
2623        let v0_back_ref_re = static_regex!(r"B[0-9a-zA-Z]_");
2624
2625        const V0_BACK_REF_PLACEHOLDER: &str = r"B<REF>_";
2626        if v0_back_ref_prefix_re.is_match(&normalized) {
2627            // Normalize back references (see RFC 2603)
2628            normalized =
2629                v0_back_ref_re.replace_all(&normalized, V0_BACK_REF_PLACEHOLDER).into_owned();
2630        }
2631
2632        // AllocId are numbered globally in a compilation session. This can lead to changes
2633        // depending on the exact compilation flags and host architecture. Meanwhile, we want
2634        // to keep them numbered, to see if the same id appears multiple times.
2635        // So we remap to deterministic numbers that only depend on the subset of allocations
2636        // that actually appear in the output.
2637        // We use uppercase ALLOC to distinguish from the non-normalized version.
2638        {
2639            match self.config.mode {
2640                // Unfortunately, due to parallel frontend assigning alloc-ids
2641                // nondeterministically we resort to dropping ids altogether for now
2642                // in ui tests
2643                TestMode::Ui => {
2644                    // The alloc-id appears in pretty-printed allocations.
2645                    normalized = static_regex!(
2646                        r"╾─*(a(lloc)?|A(LLOC)?)\d+(\+0x[0-9a-f]+)?(<imm>)?( ?\(\d+ ptr bytes\))?─*╼"
2647                    )
2648                    .replace_all(&normalized, |_: &Captures<'_>| "╾ALLOC$ID╼".to_string())
2649                    .into_owned();
2650
2651                    // The alloc-id appears in a sentence.
2652                    normalized = static_regex!(r"\b(alloc|ALLOC)\d+\b")
2653                        .replace_all(&normalized, |_: &Captures<'_>| "ALLOC$ID".to_string())
2654                        .into_owned();
2655                }
2656                // use consistent `AllocId`s in other test modes, where parallel frontend
2657                // should not (theoretically) be an issue
2658                _ => {
2659                    let mut seen_allocs = indexmap::IndexSet::new();
2660                    // The alloc-id appears in pretty-printed allocations.
2661                    normalized = static_regex!(
2662                        r"╾─*a(lloc)?([0-9]+)(\+0x[0-9a-f]+)?(<imm>)?( \([0-9]+ ptr bytes\))?─*╼"
2663                    )
2664                    .replace_all(&normalized, |caps: &Captures<'_>| {
2665                        // Renumber the captured index.
2666                        let index = caps.get(2).unwrap().as_str().to_string();
2667                        let (index, _) = seen_allocs.insert_full(index);
2668                        let offset = caps.get(3).map_or("", |c| c.as_str());
2669                        let imm = caps.get(4).map_or("", |c| c.as_str());
2670                        // Do not bother keeping it pretty, just make it deterministic.
2671                        format!("╾ALLOC{index}{offset}{imm}╼")
2672                    })
2673                    .into_owned();
2674
2675                    // The alloc-id appears in a sentence.
2676                    normalized = static_regex!(r"\balloc([0-9]+)\b")
2677                        .replace_all(&normalized, |caps: &Captures<'_>| {
2678                            let index = caps.get(1).unwrap().as_str().to_string();
2679                            let (index, _) = seen_allocs.insert_full(index);
2680                            format!("ALLOC{index}")
2681                        })
2682                        .into_owned();
2683                }
2684            }
2685        }
2686
2687        // Custom normalization rules
2688        for rule in custom_rules {
2689            let re = Regex::new(&rule.0).expect("bad regex in custom normalization rule");
2690            normalized = re.replace_all(&normalized, &rule.1[..]).into_owned();
2691        }
2692        normalized
2693    }
2694
2695    /// Normalize output differences across platforms. Generally changes Windows output to be more
2696    /// Unix-like.
2697    ///
2698    /// Replaces backslashes in paths with forward slashes, and replaces CRLF line endings
2699    /// with LF.
2700    fn normalize_platform_differences(output: &str) -> String {
2701        let output = output.replace(r"\\", r"\");
2702
2703        // Used to find Windows paths.
2704        //
2705        // It's not possible to detect paths in the error messages generally, but this is a
2706        // decent enough heuristic.
2707        let re = static_regex!(
2708            r#"(?x)
2709                (?:
2710                  # Match paths that don't include spaces.
2711                  (?:\\[\pL\pN\.\-_']+)+\.\pL+
2712                |
2713                  # If the path starts with a well-known root, then allow spaces and no file extension.
2714                  \$(?:DIR|SRC_DIR|TEST_BUILD_DIR|BUILD_DIR|LIB_DIR)(?:\\[\pL\pN\.\-_'\ ]+)+
2715                )"#
2716        );
2717        re.replace_all(&output, |caps: &Captures<'_>| caps[0].replace(r"\", "/"))
2718            .replace("\r\n", "\n")
2719    }
2720
2721    fn expected_output_path(&self, kind: &str) -> Utf8PathBuf {
2722        let mut path = expected_output_path(
2723            &self.testpaths,
2724            self.variant.revision(),
2725            &self.config.compare_mode,
2726            kind,
2727        );
2728
2729        if !path.exists() {
2730            if let Some(CompareMode::Polonius) = self.config.compare_mode {
2731                path = expected_output_path(&self.testpaths, self.variant.revision(), &None, kind);
2732            }
2733        }
2734
2735        if !path.exists() {
2736            path = expected_output_path(&self.testpaths, self.variant.revision(), &None, kind);
2737        }
2738
2739        path
2740    }
2741
2742    fn load_expected_output(&self, kind: &str) -> String {
2743        let path = self.expected_output_path(kind);
2744        if path.exists() {
2745            match self.load_expected_output_from_path(&path) {
2746                Ok(x) => x,
2747                Err(x) => self.fatal(&x),
2748            }
2749        } else {
2750            String::new()
2751        }
2752    }
2753
2754    fn load_expected_output_from_path(&self, path: &Utf8Path) -> Result<String, String> {
2755        fs::read_to_string(path)
2756            .map_err(|err| format!("failed to load expected output from `{}`: {}", path, err))
2757    }
2758
2759    /// Attempts to delete a file, succeeding if the file does not exist.
2760    fn delete_file(&self, file: &Utf8Path) {
2761        if let Err(e) = fs::remove_file(file.as_std_path())
2762            && e.kind() != io::ErrorKind::NotFound
2763        {
2764            self.fatal(&format!("failed to delete `{}`: {}", file, e,));
2765        }
2766    }
2767
2768    fn compare_output(
2769        &self,
2770        stream: &str,
2771        actual: &str,
2772        actual_unnormalized: &str,
2773        expected: &str,
2774    ) -> CompareOutcome {
2775        let expected_path = expected_output_path(
2776            self.testpaths,
2777            self.variant.revision(),
2778            &self.config.compare_mode,
2779            stream,
2780        );
2781
2782        if self.config.bless && actual.is_empty() && expected_path.exists() {
2783            self.delete_file(&expected_path);
2784        }
2785
2786        let are_different = match (self.force_color_svg(), expected.find('\n'), actual.find('\n')) {
2787            // FIXME: We ignore the first line of SVG files
2788            // because the width parameter is non-deterministic.
2789            (true, Some(nl_e), Some(nl_a)) => expected[nl_e..] != actual[nl_a..],
2790            _ => expected != actual,
2791        };
2792        if !are_different {
2793            return CompareOutcome::Same;
2794        }
2795
2796        // Wrapper tools set by `runner` might provide extra output on failure,
2797        // for example a WebAssembly runtime might print the stack trace of an
2798        // `unreachable` instruction by default.
2799        let compare_output_by_lines_subset = self.config.runner.is_some();
2800
2801        // Also, some tests like `ui/parallel-rustc` have non-deterministic
2802        // orders of output, so we need to compare by lines.
2803        let compare_output_by_lines = self.props.compare_output_by_lines;
2804
2805        let tmp;
2806        let (expected, actual): (&str, &str) = if compare_output_by_lines_subset {
2807            let actual_lines: HashSet<_> = actual.lines().collect();
2808            let expected_lines: Vec<_> = expected.lines().collect();
2809            let mut used = expected_lines.clone();
2810            used.retain(|line| actual_lines.contains(line));
2811
2812            // check if `expected` contains a subset of the lines of `actual`
2813            if used.len() == expected_lines.len() && (expected.is_empty() == actual.is_empty()) {
2814                return CompareOutcome::Same;
2815            }
2816            if expected_lines.is_empty() {
2817                // if we have no lines to check, force a full overwrite
2818                ("", actual)
2819            } else {
2820                // this prints/blesses the subset, not the actual
2821                tmp = (expected_lines.join("\n"), used.join("\n"));
2822                (&tmp.0, &tmp.1)
2823            }
2824        } else if compare_output_by_lines {
2825            let mut actual_lines = self.lines_for_comparison(actual);
2826            let mut expected_lines = self.lines_for_comparison(expected);
2827            actual_lines.sort_unstable();
2828            expected_lines.sort_unstable();
2829            if actual_lines == expected_lines {
2830                return CompareOutcome::Same;
2831            } else {
2832                (expected, actual)
2833            }
2834        } else {
2835            (expected, actual)
2836        };
2837
2838        // Write the actual output to a file in build directory.
2839        let actual_path = self
2840            .output_base_name()
2841            .with_extra_extension(self.variant.revision().unwrap_or(""))
2842            .with_extra_extension(
2843                self.config.compare_mode.as_ref().map(|cm| cm.to_str()).unwrap_or(""),
2844            )
2845            .with_extra_extension(stream);
2846
2847        if let Err(err) = fs::write(&actual_path, &actual) {
2848            self.fatal(&format!("failed to write {stream} to `{actual_path}`: {err}",));
2849        }
2850        writeln!(self.stdout, "Saved the actual {stream} to `{actual_path}`");
2851
2852        if !self.config.bless {
2853            if expected.is_empty() {
2854                writeln!(self.stdout, "normalized {}:\n{}\n", stream, actual);
2855            } else {
2856                self.show_diff(
2857                    stream,
2858                    &expected_path,
2859                    &actual_path,
2860                    expected,
2861                    actual,
2862                    actual_unnormalized,
2863                    compare_output_by_lines || compare_output_by_lines_subset,
2864                );
2865            }
2866        } else {
2867            // Delete non-revision .stderr/.stdout file if revisions are used.
2868            // Without this, we'd just generate the new files and leave the old files around.
2869            if self.variant.revision().is_some() {
2870                let old =
2871                    expected_output_path(self.testpaths, None, &self.config.compare_mode, stream);
2872                self.delete_file(&old);
2873            }
2874
2875            if !actual.is_empty() {
2876                if let Err(err) = fs::write(&expected_path, &actual) {
2877                    self.fatal(&format!("failed to write {stream} to `{expected_path}`: {err}"));
2878                }
2879                writeln!(
2880                    self.stdout,
2881                    "Blessing the {stream} of `{test_name}` as `{expected_path}`",
2882                    test_name = self.testpaths.file
2883                );
2884            }
2885        }
2886
2887        writeln!(self.stdout, "\nThe actual {stream} differed from the expected {stream}");
2888
2889        if self.config.bless { CompareOutcome::Blessed } else { CompareOutcome::Differed }
2890    }
2891
2892    /// Returns whether to show the full stderr/stdout.
2893    fn show_diff(
2894        &self,
2895        stream: &str,
2896        expected_path: &Utf8Path,
2897        actual_path: &Utf8Path,
2898        expected: &str,
2899        actual: &str,
2900        actual_unnormalized: &str,
2901        show_diff_by_lines: bool,
2902    ) {
2903        writeln!(self.stderr, "diff of {stream}:\n");
2904        if let Some(diff_command) = self.config.diff_command.as_deref() {
2905            let mut args = diff_command.split_whitespace();
2906            let name = args.next().unwrap();
2907            match Command::new(name).args(args).args([expected_path, actual_path]).output() {
2908                Err(err) => {
2909                    self.fatal(&format!(
2910                        "failed to call custom diff command `{diff_command}`: {err}"
2911                    ));
2912                }
2913                Ok(output) => {
2914                    let output = String::from_utf8_lossy(&output.stdout);
2915                    write!(self.stderr, "{output}");
2916                }
2917            }
2918        } else {
2919            write!(self.stderr, "{}", write_diff(expected, actual, 3));
2920        }
2921
2922        // NOTE: argument order is important, we need `actual` to be on the left so the line number match up when we compare it to `actual_unnormalized` below.
2923        let diff_results = make_diff(actual, expected, 0);
2924
2925        let (mut mismatches_normalized, mut mismatch_line_nos) = (String::new(), vec![]);
2926        for hunk in diff_results {
2927            let mut line_no = hunk.line_number;
2928            for line in hunk.lines {
2929                // NOTE: `Expected` is actually correct here, the argument order is reversed so our line numbers match up
2930                if let DiffLine::Expected(normalized) = line {
2931                    mismatches_normalized += &normalized;
2932                    mismatches_normalized += "\n";
2933                    mismatch_line_nos.push(line_no);
2934                    line_no += 1;
2935                }
2936            }
2937        }
2938        let mut mismatches_unnormalized = String::new();
2939        let diff_normalized = make_diff(actual, actual_unnormalized, 0);
2940        for hunk in diff_normalized {
2941            if mismatch_line_nos.contains(&hunk.line_number) {
2942                for line in hunk.lines {
2943                    if let DiffLine::Resulting(unnormalized) = line {
2944                        mismatches_unnormalized += &unnormalized;
2945                        mismatches_unnormalized += "\n";
2946                    }
2947                }
2948            }
2949        }
2950
2951        let normalized_diff = make_diff(&mismatches_normalized, &mismatches_unnormalized, 0);
2952        // HACK: instead of checking if each hunk is empty, this only checks if the whole input is empty. we should be smarter about this so we don't treat added or removed output as normalized.
2953        if !normalized_diff.is_empty()
2954            && !mismatches_unnormalized.is_empty()
2955            && !mismatches_normalized.is_empty()
2956        {
2957            writeln!(
2958                self.stderr,
2959                "Note: some mismatched output was normalized before being compared"
2960            );
2961            // FIXME: respect diff_command
2962            write!(
2963                self.stderr,
2964                "{}",
2965                write_diff(&mismatches_unnormalized, &mismatches_normalized, 0)
2966            );
2967        }
2968
2969        if show_diff_by_lines {
2970            let expected_lines = self.lines_for_comparison(expected);
2971            let actual_lines = self.lines_for_comparison(actual);
2972            write!(self.stderr, "{}", diff_by_lines(&expected_lines, &actual_lines));
2973        }
2974    }
2975
2976    fn check_and_prune_duplicate_outputs(
2977        &self,
2978        proc_res: &ProcRes,
2979        modes: &[CompareMode],
2980        require_same_modes: &[CompareMode],
2981    ) {
2982        for kind in UI_EXTENSIONS {
2983            let canon_comparison_path =
2984                expected_output_path(&self.testpaths, self.variant.revision(), &None, kind);
2985
2986            let canon = match self.load_expected_output_from_path(&canon_comparison_path) {
2987                Ok(canon) => canon,
2988                _ => continue,
2989            };
2990            let bless = self.config.bless;
2991            let check_and_prune_duplicate_outputs = |mode: &CompareMode, require_same: bool| {
2992                let examined_path = expected_output_path(
2993                    &self.testpaths,
2994                    self.variant.revision(),
2995                    &Some(mode.clone()),
2996                    kind,
2997                );
2998
2999                // If there is no output, there is nothing to do
3000                let examined_content = match self.load_expected_output_from_path(&examined_path) {
3001                    Ok(content) => content,
3002                    _ => return,
3003                };
3004
3005                let is_duplicate = canon == examined_content;
3006
3007                match (bless, require_same, is_duplicate) {
3008                    // If we're blessing and the output is the same, then delete the file.
3009                    (true, _, true) => {
3010                        self.delete_file(&examined_path);
3011                    }
3012                    // If we want them to be the same, but they are different, then error.
3013                    // We do this whether we bless or not
3014                    (_, true, false) => {
3015                        self.fatal_proc_rec(
3016                            &format!("`{}` should not have different output from base test!", kind),
3017                            proc_res,
3018                        );
3019                    }
3020                    _ => {}
3021                }
3022            };
3023            for mode in modes {
3024                check_and_prune_duplicate_outputs(mode, false);
3025            }
3026            for mode in require_same_modes {
3027                check_and_prune_duplicate_outputs(mode, true);
3028            }
3029        }
3030    }
3031
3032    fn create_stamp(&self) {
3033        let stamp_file_path = stamp_file_path(&self.config, self.testpaths, self.variant);
3034        fs::write(&stamp_file_path, compute_stamp_hash(&self.config, self.variant)).unwrap();
3035    }
3036
3037    fn init_incremental_test(&self) {
3038        // (See `run_incremental_test` for an overview of how incremental tests work.)
3039
3040        // Before any of the revisions have executed, create the
3041        // incremental workproduct directory.  Delete any old
3042        // incremental work products that may be there from prior
3043        // runs.
3044        let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
3045        if incremental_dir.exists() {
3046            // Canonicalizing the path will convert it to the //?/ format
3047            // on Windows, which enables paths longer than 260 character
3048            let canonicalized = incremental_dir.canonicalize().unwrap();
3049            fs::remove_dir_all(canonicalized).unwrap();
3050        }
3051        fs::create_dir_all(&incremental_dir).unwrap();
3052
3053        if self.config.verbose {
3054            writeln!(self.stdout, "init_incremental_test: incremental_dir={incremental_dir}");
3055        }
3056    }
3057}
3058
3059struct ProcArgs {
3060    prog: OsString,
3061    args: Vec<OsString>,
3062}
3063
3064#[derive(Debug)]
3065pub(crate) struct ProcRes {
3066    status: ExitStatus,
3067    stdout: String,
3068    stderr: String,
3069    truncated: Truncated,
3070    cmdline: String,
3071}
3072
3073impl ProcRes {
3074    #[must_use]
3075    pub(crate) fn format_info(&self) -> String {
3076        fn render(name: &str, contents: &str) -> String {
3077            let contents = json::extract_rendered(contents);
3078            let contents = contents.trim_end();
3079            if contents.is_empty() {
3080                format!("{name}: none")
3081            } else {
3082                format!(
3083                    "\
3084                     --- {name} -------------------------------\n\
3085                     {contents}\n\
3086                     ------------------------------------------",
3087                )
3088            }
3089        }
3090
3091        format!(
3092            "status: {}\ncommand: {}\n{}\n{}\n",
3093            self.status,
3094            self.cmdline,
3095            render("stdout", &self.stdout),
3096            render("stderr", &self.stderr),
3097        )
3098    }
3099}
3100
3101#[derive(Debug)]
3102enum TargetLocation {
3103    ThisFile(Utf8PathBuf),
3104    ThisDirectory(Utf8PathBuf),
3105}
3106
3107enum AllowUnused {
3108    Yes,
3109    No,
3110}
3111
3112enum LinkToAux {
3113    Yes,
3114    No,
3115}
3116
3117#[derive(Debug, PartialEq)]
3118enum AuxType {
3119    Bin,
3120    Lib,
3121    Dylib,
3122    ProcMacro,
3123}
3124
3125/// Outcome of comparing a stream to a blessed file,
3126/// e.g. `.stderr` and `.fixed`.
3127#[derive(Copy, Clone, Debug, PartialEq, Eq)]
3128enum CompareOutcome {
3129    /// Expected and actual outputs are the same
3130    Same,
3131    /// Outputs differed but were blessed
3132    Blessed,
3133    /// Outputs differed and an error should be emitted
3134    Differed,
3135}
3136
3137#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3138enum DocKind {
3139    Html,
3140    Json,
3141}
3142
3143impl CompareOutcome {
3144    fn should_error(&self) -> bool {
3145        matches!(self, CompareOutcome::Differed)
3146    }
3147}