Auto merge of #151683 - Zalathar:compiler-kind, r=jieyouxu

compiletest: Add `CompilerKind` to distinguish between rustc and rustdoc

This PR slightly improves `TestCx::make_compile_args` by using `CompilerKind` to explicitly distinguish between rustc and rustdoc.

The resulting code could still use more attention, but I think this is a good incremental improvement.

There *should* (hopefully) be no change to overall compiletest behaviour.
This commit is contained in:
bors 2026-01-27 10:12:01 +00:00
commit 94a0cd15f5
3 changed files with 157 additions and 112 deletions

View file

@ -255,6 +255,13 @@ enum Emit {
LinkArgsAsm,
}
/// Indicates whether we are using `rustc` or `rustdoc` to compile an input file.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum CompilerKind {
Rustc,
Rustdoc,
}
impl<'test> TestCx<'test> {
/// Code executed for each revision in turn (or, if there are no
/// revisions, exactly once, with revision == None).
@ -958,6 +965,8 @@ impl<'test> TestCx<'test> {
local_pm: Option<PassMode>,
passes: Vec<String>,
) -> ProcRes {
let compiler_kind = self.compiler_kind_for_non_aux();
// Only use `make_exe_name` when the test ends up being executed.
let output_file = match will_execute {
WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()),
@ -973,7 +982,7 @@ impl<'test> TestCx<'test> {
// want to actually assert warnings about all this code. Instead
// let's just ignore unused code warnings by defaults and tests
// can turn it back on if needed.
if !self.is_rustdoc()
if compiler_kind == CompilerKind::Rustc
// Note that we use the local pass mode here as we don't want
// to set unused to allow if we've overridden the pass mode
// via command line flags.
@ -988,6 +997,7 @@ impl<'test> TestCx<'test> {
};
let rustc = self.make_compile_args(
compiler_kind,
&self.testpaths.file,
output_file,
emit,
@ -1347,6 +1357,7 @@ impl<'test> TestCx<'test> {
fn build_minicore(&self) -> Utf8PathBuf {
let output_file_path = self.output_base_dir().join("libminicore.rlib");
let mut rustc = self.make_compile_args(
CompilerKind::Rustc,
&self.config.minicore_path,
TargetLocation::ThisFile(output_file_path.clone()),
Emit::None,
@ -1404,6 +1415,8 @@ impl<'test> TestCx<'test> {
// Create the directory for the stdout/stderr files.
create_dir_all(aux_cx.output_base_dir()).unwrap();
let mut aux_rustc = aux_cx.make_compile_args(
// Always use `rustc` for aux crates, even in rustdoc tests.
CompilerKind::Rustc,
&aux_path,
aux_output,
Emit::None,
@ -1554,15 +1567,41 @@ impl<'test> TestCx<'test> {
result
}
fn is_rustdoc(&self) -> bool {
matches!(
self.config.suite,
TestSuite::RustdocUi | TestSuite::RustdocJs | TestSuite::RustdocJson
)
/// Choose a compiler kind (rustc or rustdoc) for compiling test files,
/// based on the test suite being tested.
fn compiler_kind_for_non_aux(&self) -> CompilerKind {
match self.config.suite {
TestSuite::RustdocJs | TestSuite::RustdocJson | TestSuite::RustdocUi => {
CompilerKind::Rustdoc
}
// Exhaustively match all other suites.
// Note that some suites never actually use this method, so the
// return value for those suites is not necessarily meaningful.
TestSuite::AssemblyLlvm
| TestSuite::BuildStd
| TestSuite::CodegenLlvm
| TestSuite::CodegenUnits
| TestSuite::Coverage
| TestSuite::CoverageRunRustdoc
| TestSuite::Crashes
| TestSuite::Debuginfo
| TestSuite::Incremental
| TestSuite::MirOpt
| TestSuite::Pretty
| TestSuite::RunMake
| TestSuite::RunMakeCargo
| TestSuite::RustdocGui
| TestSuite::RustdocHtml
| TestSuite::RustdocJsStd
| TestSuite::Ui
| TestSuite::UiFullDeps => CompilerKind::Rustc,
}
}
fn make_compile_args(
&self,
compiler_kind: CompilerKind,
input_file: &Utf8Path,
output_file: TargetLocation,
emit: Emit,
@ -1570,17 +1609,18 @@ impl<'test> TestCx<'test> {
link_to_aux: LinkToAux,
passes: Vec<String>, // Vec of passes under mir-opt test to be dumped
) -> Command {
let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary");
let is_rustdoc = self.is_rustdoc() && !is_aux;
let mut rustc = if !is_rustdoc {
Command::new(&self.config.rustc_path)
} else {
Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
// FIXME(Zalathar): We should have a cleaner distinction between
// `rustc` flags, `rustdoc` flags, and flags shared by both.
let mut compiler = match compiler_kind {
CompilerKind::Rustc => Command::new(&self.config.rustc_path),
CompilerKind::Rustdoc => {
Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
}
};
rustc.arg(input_file);
compiler.arg(input_file);
// Use a single thread for efficiency and a deterministic error message order
rustc.arg("-Zthreads=1");
compiler.arg("-Zthreads=1");
// Hide libstd sources from ui tests to make sure we generate the stderr
// output that users will see.
@ -1590,19 +1630,19 @@ impl<'test> TestCx<'test> {
// This also has the benefit of more effectively normalizing output between different
// compilers, so that we don't have to know the `/rustc/$sha` output to normalize after the
// fact.
rustc.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
rustc.arg("-Ztranslate-remapped-path-to-local-path=no");
compiler.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
compiler.arg("-Ztranslate-remapped-path-to-local-path=no");
// Hide Cargo dependency sources from ui tests to make sure the error message doesn't
// change depending on whether $CARGO_HOME is remapped or not. If this is not present,
// when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
// source will be shown, causing a blessing hell.
rustc.arg("-Z").arg(format!(
compiler.arg("-Z").arg(format!(
"ignore-directory-in-diagnostics-source-blocks={}",
home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
));
// Similarly, vendored sources shouldn't be shown when running from a dist tarball.
rustc.arg("-Z").arg(format!(
compiler.arg("-Z").arg(format!(
"ignore-directory-in-diagnostics-source-blocks={}",
self.config.src_root.join("vendor"),
));
@ -1614,12 +1654,12 @@ impl<'test> TestCx<'test> {
&& !self.config.host_rustcflags.iter().any(|flag| flag == "--sysroot")
{
// In stage 0, make sure we use `stage0-sysroot` instead of the bootstrap sysroot.
rustc.arg("--sysroot").arg(&self.config.sysroot_base);
compiler.arg("--sysroot").arg(&self.config.sysroot_base);
}
// If the provided codegen backend is not LLVM, we need to pass it.
if let Some(ref backend) = self.config.override_codegen_backend {
rustc.arg(format!("-Zcodegen-backend={}", backend));
compiler.arg(format!("-Zcodegen-backend={}", backend));
}
// Optionally prevent default --target if specified in test compile-flags.
@ -1629,22 +1669,22 @@ impl<'test> TestCx<'test> {
let target =
if self.props.force_host { &*self.config.host } else { &*self.config.target };
rustc.arg(&format!("--target={}", target));
compiler.arg(&format!("--target={}", target));
}
self.set_revision_flags(&mut rustc);
self.set_revision_flags(&mut compiler);
if !is_rustdoc {
if compiler_kind == CompilerKind::Rustc {
if let Some(ref incremental_dir) = self.props.incremental_dir {
rustc.args(&["-C", &format!("incremental={}", incremental_dir)]);
rustc.args(&["-Z", "incremental-verify-ich"]);
compiler.args(&["-C", &format!("incremental={}", incremental_dir)]);
compiler.args(&["-Z", "incremental-verify-ich"]);
}
if self.config.mode == TestMode::CodegenUnits {
rustc.args(&["-Z", "human_readable_cgu_names"]);
compiler.args(&["-Z", "human_readable_cgu_names"]);
}
}
if self.config.optimize_tests && !is_rustdoc {
if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc {
match self.config.mode {
TestMode::Ui => {
// If optimize-tests is true we still only want to optimize tests that actually get
@ -1658,7 +1698,7 @@ impl<'test> TestCx<'test> {
.iter()
.any(|arg| arg == "-O" || arg.contains("opt-level"))
{
rustc.arg("-O");
compiler.arg("-O");
}
}
TestMode::DebugInfo => { /* debuginfo tests must be unoptimized */ }
@ -1669,7 +1709,7 @@ impl<'test> TestCx<'test> {
// compile flags (below) or in per-test `compile-flags`.
}
_ => {
rustc.arg("-O");
compiler.arg("-O");
}
}
}
@ -1690,24 +1730,24 @@ impl<'test> TestCx<'test> {
if self.props.error_patterns.is_empty()
&& self.props.regex_error_patterns.is_empty()
{
rustc.args(&["--error-format", "json"]);
rustc.args(&["--json", "future-incompat"]);
compiler.args(&["--error-format", "json"]);
compiler.args(&["--json", "future-incompat"]);
}
rustc.arg("-Zui-testing");
rustc.arg("-Zdeduplicate-diagnostics=no");
compiler.arg("-Zui-testing");
compiler.arg("-Zdeduplicate-diagnostics=no");
}
TestMode::Ui => {
if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
rustc.args(&["--error-format", "json"]);
rustc.args(&["--json", "future-incompat"]);
compiler.args(&["--error-format", "json"]);
compiler.args(&["--json", "future-incompat"]);
}
rustc.arg("-Ccodegen-units=1");
compiler.arg("-Ccodegen-units=1");
// Hide line numbers to reduce churn
rustc.arg("-Zui-testing");
rustc.arg("-Zdeduplicate-diagnostics=no");
rustc.arg("-Zwrite-long-types-to-disk=no");
compiler.arg("-Zui-testing");
compiler.arg("-Zdeduplicate-diagnostics=no");
compiler.arg("-Zwrite-long-types-to-disk=no");
// FIXME: use this for other modes too, for perf?
rustc.arg("-Cstrip=debuginfo");
compiler.arg("-Cstrip=debuginfo");
}
TestMode::MirOpt => {
// We check passes under test to minimize the mir-opt test dump
@ -1719,7 +1759,7 @@ impl<'test> TestCx<'test> {
"-Zdump-mir=all".to_string()
};
rustc.args(&[
compiler.args(&[
"-Copt-level=1",
&zdump_arg,
"-Zvalidate-mir",
@ -1729,45 +1769,46 @@ impl<'test> TestCx<'test> {
"--crate-type=rlib",
]);
if let Some(pass) = &self.props.mir_unit_test {
rustc.args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
compiler
.args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
} else {
rustc.args(&[
compiler.args(&[
"-Zmir-opt-level=4",
"-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals",
]);
}
set_mir_dump_dir(&mut rustc);
set_mir_dump_dir(&mut compiler);
}
TestMode::CoverageMap => {
rustc.arg("-Cinstrument-coverage");
compiler.arg("-Cinstrument-coverage");
// These tests only compile to LLVM IR, so they don't need the
// profiler runtime to be present.
rustc.arg("-Zno-profiler-runtime");
compiler.arg("-Zno-profiler-runtime");
// Coverage mappings are sensitive to MIR optimizations, and
// the current snapshots assume `opt-level=2` unless overridden
// by `compile-flags`.
rustc.arg("-Copt-level=2");
compiler.arg("-Copt-level=2");
}
TestMode::CoverageRun => {
rustc.arg("-Cinstrument-coverage");
compiler.arg("-Cinstrument-coverage");
// Coverage reports are sometimes sensitive to optimizations,
// and the current snapshots assume `opt-level=2` unless
// overridden by `compile-flags`.
rustc.arg("-Copt-level=2");
compiler.arg("-Copt-level=2");
}
TestMode::Assembly | TestMode::Codegen => {
rustc.arg("-Cdebug-assertions=no");
compiler.arg("-Cdebug-assertions=no");
// For assembly and codegen tests, we want to use the same order
// of the items of a codegen unit as the source order, so that
// we can compare the output with the source code through filecheck.
rustc.arg("-Zcodegen-source-order");
compiler.arg("-Zcodegen-source-order");
}
TestMode::Crashes => {
set_mir_dump_dir(&mut rustc);
set_mir_dump_dir(&mut compiler);
}
TestMode::CodegenUnits => {
rustc.arg("-Zprint-mono-items");
compiler.arg("-Zprint-mono-items");
}
TestMode::Pretty
| TestMode::DebugInfo
@ -1780,37 +1821,38 @@ impl<'test> TestCx<'test> {
}
if self.props.remap_src_base {
rustc.arg(format!(
compiler.arg(format!(
"--remap-path-prefix={}={}",
self.config.src_test_suite_root, FAKE_SRC_BASE,
));
}
match emit {
Emit::None => {}
Emit::Metadata if is_rustdoc => {}
Emit::Metadata => {
rustc.args(&["--emit", "metadata"]);
}
Emit::LlvmIr => {
rustc.args(&["--emit", "llvm-ir"]);
}
Emit::Mir => {
rustc.args(&["--emit", "mir"]);
}
Emit::Asm => {
rustc.args(&["--emit", "asm"]);
}
Emit::LinkArgsAsm => {
rustc.args(&["-Clink-args=--emit=asm"]);
if compiler_kind == CompilerKind::Rustc {
match emit {
Emit::None => {}
Emit::Metadata => {
compiler.args(&["--emit", "metadata"]);
}
Emit::LlvmIr => {
compiler.args(&["--emit", "llvm-ir"]);
}
Emit::Mir => {
compiler.args(&["--emit", "mir"]);
}
Emit::Asm => {
compiler.args(&["--emit", "asm"]);
}
Emit::LinkArgsAsm => {
compiler.args(&["-Clink-args=--emit=asm"]);
}
}
}
if !is_rustdoc {
if compiler_kind == CompilerKind::Rustc {
if self.config.target == "wasm32-unknown-unknown" || self.is_vxworks_pure_static() {
// rustc.arg("-g"); // get any backtrace at all on errors
} else if !self.props.no_prefer_dynamic {
rustc.args(&["-C", "prefer-dynamic"]);
compiler.args(&["-C", "prefer-dynamic"]);
}
}
@ -1819,36 +1861,37 @@ impl<'test> TestCx<'test> {
// avoid a compiler warning about `--out-dir` being ignored.
_ if self.props.compile_flags.iter().any(|flag| flag == "-o") => {}
TargetLocation::ThisFile(path) => {
rustc.arg("-o").arg(path);
compiler.arg("-o").arg(path);
}
TargetLocation::ThisDirectory(path) => {
if is_rustdoc {
TargetLocation::ThisDirectory(path) => match compiler_kind {
CompilerKind::Rustdoc => {
// `rustdoc` uses `-o` for the output directory.
rustc.arg("-o").arg(path);
} else {
rustc.arg("--out-dir").arg(path);
compiler.arg("-o").arg(path);
}
}
CompilerKind::Rustc => {
compiler.arg("--out-dir").arg(path);
}
},
}
match self.config.compare_mode {
Some(CompareMode::Polonius) => {
rustc.args(&["-Zpolonius=next"]);
compiler.args(&["-Zpolonius=next"]);
}
Some(CompareMode::NextSolver) => {
rustc.args(&["-Znext-solver"]);
compiler.args(&["-Znext-solver"]);
}
Some(CompareMode::NextSolverCoherence) => {
rustc.args(&["-Znext-solver=coherence"]);
compiler.args(&["-Znext-solver=coherence"]);
}
Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => {
rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
compiler.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
}
Some(CompareMode::SplitDwarf) => {
rustc.args(&["-Csplit-debuginfo=unpacked"]);
compiler.args(&["-Csplit-debuginfo=unpacked"]);
}
Some(CompareMode::SplitDwarfSingle) => {
rustc.args(&["-Csplit-debuginfo=packed"]);
compiler.args(&["-Csplit-debuginfo=packed"]);
}
None => {}
}
@ -1857,44 +1900,44 @@ impl<'test> TestCx<'test> {
// overwrite this.
// Don't allow `unused_attributes` since these are usually actual mistakes, rather than just unused code.
if let AllowUnused::Yes = allow_unused {
rustc.args(&["-A", "unused", "-W", "unused_attributes"]);
compiler.args(&["-A", "unused", "-W", "unused_attributes"]);
}
// Allow tests to use internal features.
rustc.args(&["-A", "internal_features"]);
compiler.args(&["-A", "internal_features"]);
// Allow tests to have unused parens and braces.
// Add #![deny(unused_parens, unused_braces)] to the test file if you want to
// test that these lints are working.
rustc.args(&["-A", "unused_parens"]);
rustc.args(&["-A", "unused_braces"]);
compiler.args(&["-A", "unused_parens"]);
compiler.args(&["-A", "unused_braces"]);
if self.props.force_host {
self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags);
if !is_rustdoc {
if let Some(ref linker) = self.config.host_linker {
rustc.arg(format!("-Clinker={}", linker));
}
self.maybe_add_external_args(&mut compiler, &self.config.host_rustcflags);
if compiler_kind == CompilerKind::Rustc
&& let Some(ref linker) = self.config.host_linker
{
compiler.arg(format!("-Clinker={linker}"));
}
} else {
self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
if !is_rustdoc {
if let Some(ref linker) = self.config.target_linker {
rustc.arg(format!("-Clinker={}", linker));
}
self.maybe_add_external_args(&mut compiler, &self.config.target_rustcflags);
if compiler_kind == CompilerKind::Rustc
&& let Some(ref linker) = self.config.target_linker
{
compiler.arg(format!("-Clinker={linker}"));
}
}
// Use dynamic musl for tests because static doesn't allow creating dylibs
if self.config.host.contains("musl") || self.is_vxworks_pure_dynamic() {
rustc.arg("-Ctarget-feature=-crt-static");
compiler.arg("-Ctarget-feature=-crt-static");
}
if let LinkToAux::Yes = link_to_aux {
// if we pass an `-L` argument to a directory that doesn't exist,
// macOS ld emits warnings which disrupt the .stderr files
if self.has_aux_dir() {
rustc.arg("-L").arg(self.aux_output_dir_name());
compiler.arg("-L").arg(self.aux_output_dir_name());
}
}
@ -1908,13 +1951,13 @@ impl<'test> TestCx<'test> {
//
// `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics.
if self.props.add_minicore {
rustc.arg("-Cpanic=abort");
rustc.arg("-Cforce-unwind-tables=yes");
compiler.arg("-Cpanic=abort");
compiler.arg("-Cforce-unwind-tables=yes");
}
rustc.args(&self.props.compile_flags);
compiler.args(&self.props.compile_flags);
rustc
compiler
}
fn make_exe_name(&self) -> Utf8PathBuf {
@ -2127,6 +2170,7 @@ impl<'test> TestCx<'test> {
let output_path = self.output_base_name().with_extension("ll");
let input_file = &self.testpaths.file;
let rustc = self.make_compile_args(
CompilerKind::Rustc,
input_file,
TargetLocation::ThisFile(output_path.clone()),
Emit::LlvmIr,

View file

@ -1,6 +1,6 @@
use camino::Utf8PathBuf;
use super::{AllowUnused, Emit, LinkToAux, ProcRes, TargetLocation, TestCx};
use crate::runtest::{AllowUnused, CompilerKind, Emit, LinkToAux, ProcRes, TargetLocation, TestCx};
impl TestCx<'_> {
pub(super) fn run_assembly_test(&self) {
@ -35,6 +35,7 @@ impl TestCx<'_> {
};
let rustc = self.make_compile_args(
CompilerKind::Rustc,
input_file,
TargetLocation::ThisFile(output_path.clone()),
emit,

View file

@ -5,12 +5,11 @@ use std::io::Write;
use rustfix::{Filter, apply_suggestions, get_suggestions_from_json};
use tracing::debug;
use super::{
AllowUnused, Emit, FailMode, LinkToAux, PassMode, RunFailMode, RunResult, TargetLocation,
TestCx, TestOutput, Truncated, UI_FIXED, WillExecute,
};
use crate::json;
use crate::runtest::ProcRes;
use crate::runtest::{
AllowUnused, Emit, FailMode, LinkToAux, PassMode, ProcRes, RunFailMode, RunResult,
TargetLocation, TestCx, TestOutput, Truncated, UI_FIXED, WillExecute,
};
impl TestCx<'_> {
pub(super) fn run_ui_test(&self) {
@ -228,6 +227,7 @@ impl TestCx<'_> {
// And finally, compile the fixed code and make sure it both
// succeeds and has no diagnostics.
let mut rustc = self.make_compile_args(
self.compiler_kind_for_non_aux(),
&self.expected_output_path(UI_FIXED),
TargetLocation::ThisFile(self.make_exe_name()),
emit_metadata,