@@ -39,6 +39,7 @@ impl DocTestRunner {
39
39
doctest : & DocTestBuilder ,
40
40
scraped_test : & ScrapedDocTest ,
41
41
target_str : & str ,
42
+ opts : & RustdocOptions ,
42
43
) {
43
44
let ignore = match scraped_test. langstr . ignore {
44
45
Ignore :: All => true ,
@@ -62,6 +63,7 @@ impl DocTestRunner {
62
63
self . nb_tests,
63
64
& mut self . output,
64
65
& mut self . output_merged_tests,
66
+ opts,
65
67
) ,
66
68
) ) ;
67
69
self . supports_color &= doctest. supports_color ;
@@ -127,20 +129,34 @@ mod __doctest_mod {{
127
129
128
130
pub static BINARY_PATH: OnceLock<PathBuf> = OnceLock::new();
129
131
pub const RUN_OPTION: &str = \" RUSTDOC_DOCTEST_RUN_NB_TEST\" ;
132
+ // On some platforms, exit code can only be 0 or 1, preventing us to actually know if it's a
133
+ // panic. In this case, we ignore the tests with `should_panic`.
134
+ //
135
+ // Emscripten can catch panics but other wasm targets cannot
136
+ pub const SHOULD_PANIC_DISABLED: bool = (
137
+ cfg!(target_family = \" wasm\" ) || cfg!(target_os = \" zkvm\" )
138
+ ) && !cfg!(target_os = \" emscripten\" );
130
139
131
140
#[allow(unused)]
132
141
pub fn doctest_path() -> Option<&'static PathBuf> {{
133
142
self::BINARY_PATH.get()
134
143
}}
135
144
136
145
#[allow(unused)]
137
- pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> ExitCode {{
146
+ pub fn doctest_runner(bin: &std::path::Path, test_nb: usize, should_panic: bool ) -> ExitCode {{
138
147
let out = std::process::Command::new(bin)
139
148
.env(self::RUN_OPTION, test_nb.to_string())
140
149
.args(std::env::args().skip(1).collect::<Vec<_>>())
141
150
.output()
142
151
.expect(\" failed to run command\" );
143
- if !out.status.success() {{
152
+ if should_panic {{
153
+ if out.status.code() != Some(test::ERROR_EXIT_CODE) {{
154
+ eprintln!(\" Test didn't panic, but it's marked `should_panic`.\" );
155
+ ExitCode::FAILURE
156
+ }} else {{
157
+ ExitCode::SUCCESS
158
+ }}
159
+ }} else if !out.status.success() {{
144
160
if let Some(code) = out.status.code() {{
145
161
eprintln!(\" Test executable failed (exit status: {{code}}).\" );
146
162
}} else {{
@@ -223,6 +239,7 @@ fn generate_mergeable_doctest(
223
239
id : usize ,
224
240
output : & mut String ,
225
241
output_merged_tests : & mut String ,
242
+ opts : & RustdocOptions ,
226
243
) -> String {
227
244
let test_id = format ! ( "__doctest_{id}" ) ;
228
245
@@ -256,22 +273,22 @@ fn main() {returns_result} {{
256
273
)
257
274
. unwrap ( ) ;
258
275
}
259
- let not_running = ignore || scraped_test. langstr . no_run ;
276
+ let should_panic = scraped_test. langstr . should_panic ;
277
+ let not_running = ignore || scraped_test. no_run ( opts) ;
260
278
writeln ! (
261
279
output_merged_tests,
262
280
"
263
281
mod {test_id} {{
264
282
pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest(
265
- {test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic} ,
283
+ {test_name:?}, {ignore} || ({should_panic} && crate::__doctest_mod::SHOULD_PANIC_DISABLED) , {file:?}, {line}, {no_run}, false ,
266
284
test::StaticTestFn(
267
285
|| {{{runner}}},
268
286
));
269
287
}}" ,
270
288
test_name = scraped_test. name,
271
289
file = scraped_test. path( ) ,
272
290
line = scraped_test. line,
273
- no_run = scraped_test. langstr. no_run,
274
- should_panic = !scraped_test. langstr. no_run && scraped_test. langstr. should_panic,
291
+ no_run = scraped_test. no_run( opts) ,
275
292
// Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply
276
293
// don't give it the function to run.
277
294
runner = if not_running {
@@ -280,7 +297,7 @@ test::StaticTestFn(
280
297
format!(
281
298
"
282
299
if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{
283
- test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}))
300
+ test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}, {should_panic} ))
284
301
}} else {{
285
302
test::assert_test_result(doctest_bundle::{test_id}::__main_fn())
286
303
}}
0 commit comments