@@ -39,6 +39,7 @@ impl DocTestRunner {
3939 doctest : & DocTestBuilder ,
4040 scraped_test : & ScrapedDocTest ,
4141 target_str : & str ,
42+ opts : & RustdocOptions ,
4243 ) {
4344 let ignore = match scraped_test. langstr . ignore {
4445 Ignore :: All => true ,
@@ -62,6 +63,7 @@ impl DocTestRunner {
6263 self . nb_tests,
6364 & mut self . output,
6465 & mut self . output_merged_tests,
66+ opts,
6567 ) ,
6668 ) ) ;
6769 self . supports_color &= doctest. supports_color ;
@@ -127,20 +129,30 @@ mod __doctest_mod {{
127129
128130 pub static BINARY_PATH: OnceLock<PathBuf> = OnceLock::new();
129131 pub const RUN_OPTION: &str = \" RUSTDOC_DOCTEST_RUN_NB_TEST\" ;
132+ pub const SHOULD_PANIC_DISABLED: bool = (
133+ cfg!(target_family = \" wasm\" ) || cfg!(target_os = \" zkvm\" )
134+ ) && !cfg!(target_os = \" emscripten\" );
130135
131136 #[allow(unused)]
132137 pub fn doctest_path() -> Option<&'static PathBuf> {{
133138 self::BINARY_PATH.get()
134139 }}
135140
136141 #[allow(unused)]
137- pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> ExitCode {{
142+ pub fn doctest_runner(bin: &std::path::Path, test_nb: usize, should_panic: bool ) -> ExitCode {{
138143 let out = std::process::Command::new(bin)
139144 .env(self::RUN_OPTION, test_nb.to_string())
140145 .args(std::env::args().skip(1).collect::<Vec<_>>())
141146 .output()
142147 .expect(\" failed to run command\" );
143- if !out.status.success() {{
148+ 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
154+ }}
155+ }} else if !out.status.success() {{
144156 if let Some(code) = out.status.code() {{
145157 eprintln!(\" Test executable failed (exit status: {{code}}).\" );
146158 }} else {{
@@ -223,6 +235,7 @@ fn generate_mergeable_doctest(
223235 id : usize ,
224236 output : & mut String ,
225237 output_merged_tests : & mut String ,
238+ opts : & RustdocOptions ,
226239) -> String {
227240 let test_id = format ! ( "__doctest_{id}" ) ;
228241
@@ -256,31 +269,33 @@ fn main() {returns_result} {{
256269 )
257270 . unwrap ( ) ;
258271 }
259- let not_running = ignore || scraped_test. langstr . no_run ;
272+ let should_panic = scraped_test. langstr . should_panic ;
273+ let not_running = ignore || scraped_test. no_run ( opts) ;
260274 writeln ! (
261275 output_merged_tests,
262276 "
263277mod {test_id} {{
264278pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest(
265- {test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic} ,
279+ {test_name:?}, {ignore} || ({should_panic} && crate::__doctest_mod::SHOULD_PANIC_DISABLED) , {file:?}, {line}, {no_run}, false ,
266280test::StaticTestFn(
267281 || {{{runner}}},
268282));
269283}}" ,
270284 test_name = scraped_test. name,
271285 file = scraped_test. path( ) ,
272286 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,
287+ no_run = scraped_test. no_run( opts) ,
275288 // Setting `no_run` to `true` in `TestDesc` still makes the test run, so we simply
276289 // don't give it the function to run.
277290 runner = if not_running {
278291 "test::assert_test_result(Ok::<(), String>(()))" . to_string( )
279292 } else {
280293 format!(
281294 "
282- if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{
283- test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}))
295+ if {should_panic} && crate::__doctest_mod::SHOULD_PANIC_DISABLED {{
296+ test::assert_test_result(Ok::<(), String>(()))
297+ }} else if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{
298+ test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id}, {should_panic}))
284299}} else {{
285300 test::assert_test_result(doctest_bundle::{test_id}::__main_fn())
286301}}
0 commit comments