From 2ddf0ca9f5402ce6a710d35a5beb2067201ff23d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 2 Aug 2025 12:44:51 +1000 Subject: [PATCH 1/3] Change `ProcRes::print_info` to `format_info` This method now returns a string instead of printing directly to (possibly-captured) stdout. --- src/tools/compiletest/src/runtest.rs | 9 +++++---- src/tools/compiletest/src/runtest/rustdoc_json.rs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index ba7fcadbc2e59..12a5e55e77b4c 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2957,7 +2957,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(); @@ -2973,20 +2974,20 @@ 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(); + println!("{}", self.format_info()); on_failure(); // Use resume_unwind instead of panic!() to prevent a panic message + backtrace from // compiletest, which is unnecessary noise. diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs index 4f35efedfde49..7c23665c39026 100644 --- a/src/tools/compiletest/src/runtest/rustdoc_json.rs +++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs @@ -31,7 +31,7 @@ impl TestCx<'_> { if !res.status.success() { self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| { println!("Rustdoc Output:"); - proc_res.print_info(); + println!("{}", proc_res.format_info()); }) } From d1d44d44f15f32ea4c53abc2dbb35bd8304e582e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 2 Aug 2025 12:57:30 +1000 Subject: [PATCH 2/3] Consolidate all `ProcRes` unwinds into one code path --- src/tools/compiletest/src/runtest.rs | 50 ++++++++++--------- src/tools/compiletest/src/runtest/rustdoc.rs | 4 +- .../compiletest/src/runtest/rustdoc_json.rs | 2 +- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 12a5e55e77b4c..b1f8e461aef8a 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -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 { @@ -2010,18 +2007,34 @@ impl<'test> TestCx<'test> { } 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)); + + // 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) @@ -2080,7 +2093,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; } @@ -2982,17 +2995,6 @@ impl ProcRes { render("stderr", &self.stderr), ) } - - pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! { - if let Some(e) = err { - println!("\nerror: {}", e); - } - println!("{}", self.format_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(())); - } } #[derive(Debug)] diff --git a/src/tools/compiletest/src/runtest/rustdoc.rs b/src/tools/compiletest/src/runtest/rustdoc.rs index 236f021ce827c..4a143aa5824e7 100644 --- a/src/tools/compiletest/src/runtest/rustdoc.rs +++ b/src/tools/compiletest/src/runtest/rustdoc.rs @@ -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); }); } } diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs index 7c23665c39026..083398f927455 100644 --- a/src/tools/compiletest/src/runtest/rustdoc_json.rs +++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs @@ -29,7 +29,7 @@ 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:"); println!("{}", proc_res.format_info()); }) From 1063b0f09010ffae99b6a3b55bec5539876ce57e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 2 Aug 2025 13:07:01 +1000 Subject: [PATCH 3/3] Change `TestCx::error` to `error_prefix`, which returns a string This reduces the amount of "hidden" printing in error-reporting code, which will be helpful when overhauling compiletest's error handling and output capture. --- src/tools/compiletest/src/runtest.rs | 36 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index b1f8e461aef8a..35670ba89e997 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -603,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); } @@ -821,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(); @@ -854,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 ¬_found { print_error(error); let mut suggestions = Vec::new(); @@ -1992,16 +1997,19 @@ 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"); } @@ -2019,7 +2027,7 @@ impl<'test> TestCx<'test> { proc_res: &ProcRes, callback_before_unwind: impl FnOnce(), ) -> ! { - self.error(err); + 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 {