Skip to content

Commit b70e20a

Browse files
Correctly handle -C panic=abort in doctests
1 parent 560d450 commit b70e20a

File tree

3 files changed

+77
-15
lines changed

3 files changed

+77
-15
lines changed

src/librustdoc/doctest.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ mod rust;
77
use std::fs::File;
88
use std::hash::{Hash, Hasher};
99
use std::io::{self, Write};
10+
#[cfg(unix)]
11+
use std::os::unix::process::ExitStatusExt;
1012
use std::path::{Path, PathBuf};
1113
use std::process::{self, Command, Stdio};
1214
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -849,12 +851,39 @@ fn run_test(
849851
} else {
850852
cmd.output()
851853
};
854+
855+
// FIXME: Make `test::get_result_from_exit_code` public and use this code instead of this.
856+
//
857+
// On Zircon (the Fuchsia kernel), an abort from userspace calls the
858+
// LLVM implementation of __builtin_trap(), e.g., ud2 on x86, which
859+
// raises a kernel exception. If a userspace process does not
860+
// otherwise arrange exception handling, the kernel kills the process
861+
// with this return code.
862+
#[cfg(target_os = "fuchsia")]
863+
const ZX_TASK_RETCODE_EXCEPTION_KILL: i32 = -1028;
864+
// On Windows we use __fastfail to abort, which is documented to use this
865+
// exception code.
866+
#[cfg(windows)]
867+
const STATUS_FAIL_FAST_EXCEPTION: i32 = 0xC0000409u32 as i32;
868+
#[cfg(unix)]
869+
const SIGABRT: std::ffi::c_int = 6;
852870
match result {
853871
Err(e) => return (duration, Err(TestFailure::ExecutionError(e))),
854872
Ok(out) => {
855-
if langstr.should_panic && out.status.code() != Some(test::ERROR_EXIT_CODE) {
856-
return (duration, Err(TestFailure::UnexpectedRunPass));
857-
} else if !langstr.should_panic && !out.status.success() {
873+
if langstr.should_panic {
874+
match out.status.code() {
875+
Some(test::ERROR_EXIT_CODE) => {}
876+
#[cfg(windows)]
877+
Some(STATUS_FAIL_FAST_EXCEPTION) => {}
878+
#[cfg(unix)]
879+
None if out.status.signal() == Some(SIGABRT) => {}
880+
// Upon an abort, Fuchsia returns the status code
881+
// `ZX_TASK_RETCODE_EXCEPTION_KILL`.
882+
#[cfg(target_os = "fuchsia")]
883+
Some(ZX_TASK_RETCODE_EXCEPTION_KILL) => {}
884+
_ => return (duration, Err(TestFailure::UnexpectedRunPass)),
885+
}
886+
} else if !out.status.success() {
858887
return (duration, Err(TestFailure::ExecutionFailure(out)));
859888
}
860889
}

src/librustdoc/doctest/runner.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,11 @@ impl DocTestRunner {
123123
{output}
124124
125125
mod __doctest_mod {{
126-
use std::sync::OnceLock;
126+
#[cfg(unix)]
127+
use std::os::unix::process::ExitStatusExt;
127128
use std::path::PathBuf;
128129
use std::process::ExitCode;
130+
use std::sync::OnceLock;
129131
130132
pub static BINARY_PATH: OnceLock<PathBuf> = OnceLock::new();
131133
pub const RUN_OPTION: &str = \"RUSTDOC_DOCTEST_RUN_NB_TEST\";
@@ -146,11 +148,35 @@ mod __doctest_mod {{
146148
.output()
147149
.expect(\"failed to run command\");
148150
if should_panic {{
149-
if out.status.code() != Some(test::ERROR_EXIT_CODE) {{
150-
eprintln!(\"Test didn't panic, but it's marked `should_panic`.\");
151-
ExitCode::FAILURE
152-
}} else {{
153-
ExitCode::SUCCESS
151+
// FIXME: Make `test::get_result_from_exit_code` public and use this code instead of this.
152+
//
153+
// On Zircon (the Fuchsia kernel), an abort from userspace calls the
154+
// LLVM implementation of __builtin_trap(), e.g., ud2 on x86, which
155+
// raises a kernel exception. If a userspace process does not
156+
// otherwise arrange exception handling, the kernel kills the process
157+
// with this return code.
158+
#[cfg(target_os = \"fuchsia\")]
159+
const ZX_TASK_RETCODE_EXCEPTION_KILL: i32 = -1028;
160+
// On Windows we use __fastfail to abort, which is documented to use this
161+
// exception code.
162+
#[cfg(windows)]
163+
const STATUS_FAIL_FAST_EXCEPTION: i32 = 0xC0000409u32 as i32;
164+
#[cfg(unix)]
165+
const SIGABRT: std::ffi::c_int = 6;
166+
167+
match out.status.code() {{
168+
Some(test::ERROR_EXIT_CODE) => ExitCode::SUCCESS,
169+
#[cfg(windows)]
170+
Some(STATUS_FAIL_FAST_EXCEPTION) => ExitCode::SUCCESS,
171+
#[cfg(unix)]
172+
None if out.status.signal() == Some(SIGABRT) => ExitCode::SUCCESS,
173+
// Upon an abort, Fuchsia returns the status code ZX_TASK_RETCODE_EXCEPTION_KILL.
174+
#[cfg(target_os = \"fuchsia\")]
175+
Some(ZX_TASK_RETCODE_EXCEPTION_KILL) => ExitCode::SUCCESS,
176+
_ => {{
177+
eprintln!(\"Test didn't panic, but it's marked `should_panic`.\");
178+
ExitCode::FAILURE
179+
}}
154180
}}
155181
}} else if !out.status.success() {{
156182
if let Some(code) = out.status.code() {{

tests/run-make/rustdoc-should-panic/rmake.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55

66
use run_make_support::rustdoc;
77

8-
fn check_output(output: String, edition: &str) {
8+
fn check_output(edition: &str, panic_abort: bool) {
9+
let mut rustdoc_cmd = rustdoc();
10+
rustdoc_cmd.input("test.rs").arg("--test").edition(edition);
11+
if panic_abort {
12+
rustdoc_cmd.args(["-C", "panic=abort"]);
13+
}
14+
let output = rustdoc_cmd.run_fail().stdout_utf8();
915
let should_contain = &[
1016
"test test.rs - bad_exit_code (line 1) ... FAILED",
1117
"test test.rs - did_not_panic (line 6) ... FAILED",
@@ -26,11 +32,12 @@ Test didn't panic, but it's marked `should_panic`.",
2632
}
2733

2834
fn main() {
29-
check_output(rustdoc().input("test.rs").arg("--test").run_fail().stdout_utf8(), "2015");
35+
check_output("2015", false);
3036

3137
// Same check with the merged doctest feature (enabled with the 2024 edition).
32-
check_output(
33-
rustdoc().input("test.rs").arg("--test").edition("2024").run_fail().stdout_utf8(),
34-
"2024",
35-
);
38+
check_output("2024", false);
39+
40+
// Checking that `-C panic=abort` is working too.
41+
check_output("2015", true);
42+
check_output("2024", true);
3643
}

0 commit comments

Comments
 (0)