Skip to content

compiletest: Preliminary cleanup of ProcRes printing/unwinding #144805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 52 additions & 41 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,13 +354,10 @@ impl<'test> TestCx<'test> {
}
} else {
if proc_res.status.success() {
{
self.error(&format!("{} test did not emit an error", self.config.mode));
if self.config.mode == crate::common::TestMode::Ui {
println!("note: by default, ui tests are expected not to compile");
}
proc_res.fatal(None, || ());
};
let err = &format!("{} test did not emit an error", self.config.mode);
let extra_note = (self.config.mode == crate::common::TestMode::Ui)
.then_some("note: by default, ui tests are expected not to compile");
self.fatal_proc_rec_general(err, extra_note, proc_res, || ());
}

if !self.props.dont_check_failure_status {
Expand Down Expand Up @@ -606,7 +603,10 @@ impl<'test> TestCx<'test> {
);
} else {
for pattern in missing_patterns {
self.error(&format!("error pattern '{}' not found!", pattern));
println!(
"\n{prefix}: error pattern '{pattern}' not found!",
prefix = self.error_prefix()
);
}
self.fatal_proc_rec("multiple error patterns not found", proc_res);
}
Expand Down Expand Up @@ -824,10 +824,11 @@ impl<'test> TestCx<'test> {
// - only known line - meh, but suggested
// - others are not worth suggesting
if !unexpected.is_empty() {
self.error(&format!(
"{} diagnostics reported in JSON output but not expected in test file",
unexpected.len(),
));
println!(
"\n{prefix}: {n} diagnostics reported in JSON output but not expected in test file",
prefix = self.error_prefix(),
n = unexpected.len(),
);
for error in &unexpected {
print_error(error);
let mut suggestions = Vec::new();
Expand Down Expand Up @@ -857,10 +858,11 @@ impl<'test> TestCx<'test> {
}
}
if !not_found.is_empty() {
self.error(&format!(
"{} diagnostics expected in test file but not reported in JSON output",
not_found.len()
));
println!(
"\n{prefix}: {n} diagnostics expected in test file but not reported in JSON output",
prefix = self.error_prefix(),
n = not_found.len(),
);
for error in &not_found {
print_error(error);
let mut suggestions = Vec::new();
Expand Down Expand Up @@ -1995,33 +1997,52 @@ impl<'test> TestCx<'test> {
output_base_name(self.config, self.testpaths, self.safe_revision())
}

fn error(&self, err: &str) {
/// Prefix to print before error messages. Normally just `error`, but also
/// includes the revision name for tests that use revisions.
#[must_use]
fn error_prefix(&self) -> String {
match self.revision {
Some(rev) => println!("\nerror in revision `{}`: {}", rev, err),
None => println!("\nerror: {}", err),
Some(rev) => format!("error in revision `{rev}`"),
None => format!("error"),
}
}

#[track_caller]
fn fatal(&self, err: &str) -> ! {
self.error(err);
println!("\n{prefix}: {err}", prefix = self.error_prefix());
error!("fatal error, panic: {:?}", err);
panic!("fatal error");
}

fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
self.error(err);
proc_res.fatal(None, || ());
self.fatal_proc_rec_general(err, None, proc_res, || ());
}

fn fatal_proc_rec_with_ctx(
/// Underlying implementation of [`Self::fatal_proc_rec`], providing some
/// extra capabilities not needed by most callers.
fn fatal_proc_rec_general(
&self,
err: &str,
extra_note: Option<&str>,
proc_res: &ProcRes,
on_failure: impl FnOnce(Self),
callback_before_unwind: impl FnOnce(),
) -> ! {
self.error(err);
proc_res.fatal(None, || on_failure(*self));
println!("\n{prefix}: {err}", prefix = self.error_prefix());

// Some callers want to print additional notes after the main error message.
if let Some(note) = extra_note {
println!("{note}");
}

// Print the details and output of the subprocess that caused this test to fail.
println!("{}", proc_res.format_info());

// Some callers want print more context or show a custom diff before the unwind occurs.
callback_before_unwind();

// Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
// compiletest, which is unnecessary noise.
std::panic::resume_unwind(Box::new(()));
}

// codegen tests (using FileCheck)
Expand Down Expand Up @@ -2080,7 +2101,7 @@ impl<'test> TestCx<'test> {
if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" }
}

fn compare_to_default_rustdoc(&mut self, out_dir: &Utf8Path) {
fn compare_to_default_rustdoc(&self, out_dir: &Utf8Path) {
if !self.config.has_html_tidy {
return;
}
Expand Down Expand Up @@ -2957,7 +2978,8 @@ pub struct ProcRes {
}

impl ProcRes {
pub fn print_info(&self) {
#[must_use]
pub fn format_info(&self) -> String {
fn render(name: &str, contents: &str) -> String {
let contents = json::extract_rendered(contents);
let contents = contents.trim_end();
Expand All @@ -2973,24 +2995,13 @@ impl ProcRes {
}
}

println!(
format!(
"status: {}\ncommand: {}\n{}\n{}\n",
self.status,
self.cmdline,
render("stdout", &self.stdout),
render("stderr", &self.stderr),
);
}

pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! {
if let Some(e) = err {
println!("\nerror: {}", e);
}
self.print_info();
on_failure();
// Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
// compiletest, which is unnecessary noise.
std::panic::resume_unwind(Box::new(()));
)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/tools/compiletest/src/runtest/rustdoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ impl TestCx<'_> {
}
let res = self.run_command_to_procres(&mut cmd);
if !res.status.success() {
self.fatal_proc_rec_with_ctx("htmldocck failed!", &res, |mut this| {
this.compare_to_default_rustdoc(&out_dir)
self.fatal_proc_rec_general("htmldocck failed!", None, &res, || {
self.compare_to_default_rustdoc(&out_dir);
});
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/tools/compiletest/src/runtest/rustdoc_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ impl TestCx<'_> {
);

if !res.status.success() {
self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| {
self.fatal_proc_rec_general("jsondocck failed!", None, &res, || {
println!("Rustdoc Output:");
proc_res.print_info();
println!("{}", proc_res.format_info());
})
}

Expand Down
Loading