@@ -407,7 +407,7 @@ pub(crate) fn run_tests(
407407 // We failed to compile all compatible tests as one so we push them into the
408408 // `standalone_tests` doctests.
409409 debug!("Failed to compile compatible doctests for edition {} all at once", edition);
410- for (pos, ( doctest, scraped_test)) in doctests.into_iter().enumerate() {
410+ for (doctest, scraped_test) in doctests {
411411 doctest.generate_unique_doctest(
412412 &scraped_test.text,
413413 scraped_test.langstr.test_harness,
@@ -420,7 +420,6 @@ pub(crate) fn run_tests(
420420 opts.clone(),
421421 Arc::clone(rustdoc_options),
422422 unused_extern_reports.clone(),
423- pos,
424423 ));
425424 }
426425 }
@@ -550,44 +549,33 @@ pub(crate) struct RunnableDocTest {
550549}
551550
552551impl RunnableDocTest {
553- fn path_for_merged_doctest_bundle(&self, id: Option<usize>) -> PathBuf {
554- let name = if let Some(id) = id {
555- format!("doctest_bundle_id_{id}.rs")
556- } else {
557- format!("doctest_bundle_{}.rs", self.edition)
558- };
559- self.test_opts.outdir.path().join(name)
552+ fn path_for_merged_doctest_bundle(&self) -> PathBuf {
553+ self.test_opts.outdir.path().join(format!("doctest_bundle_{}.rs", self.edition))
560554 }
561- fn path_for_merged_doctest_runner(&self, id: Option<usize>) -> PathBuf {
562- let name = if let Some(id) = id {
563- format!("doctest_runner_id_{id}.rs")
564- } else {
565- format!("doctest_runner_{}.rs", self.edition)
566- };
567- self.test_opts.outdir.path().join(name)
555+ fn path_for_merged_doctest_runner(&self) -> PathBuf {
556+ self.test_opts.outdir.path().join(format!("doctest_runner_{}.rs", self.edition))
568557 }
569558 fn is_multiple_tests(&self) -> bool {
570559 self.merged_test_code.is_some()
571560 }
572561}
573562
574563fn compile_merged_doctest_and_caller_binary(
575- mut child: process::Child,
564+ child: process::Child,
576565 doctest: &RunnableDocTest,
577566 rustdoc_options: &RustdocOptions,
578567 rustc_binary: &Path,
579568 output_file: &Path,
580569 compiler_args: Vec<String>,
581570 test_code: &str,
582571 instant: Instant,
583- id: Option<usize> ,
572+ is_compile_fail: bool ,
584573) -> Result<process::Output, (Duration, Result<(), RustdocResult>)> {
585574 // compile-fail tests never get merged, so this should always pass
586- let status = child.wait().expect("Failed to wait");
587-
588- // the actual test runner is a separate component, built with nightly-only features;
589- // build it now
590- let runner_input_file = doctest.path_for_merged_doctest_runner(id);
575+ let output = child.wait_with_output().expect("Failed to wait");
576+ if is_compile_fail && !output.status.success() {
577+ return Ok(output);
578+ }
591579
592580 let mut runner_compiler =
593581 wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);
@@ -596,13 +584,10 @@ fn compile_merged_doctest_and_caller_binary(
596584 runner_compiler.env("RUSTC_BOOTSTRAP", "1");
597585 runner_compiler.args(compiler_args);
598586 runner_compiler.args(["--crate-type=bin", "-o"]).arg(output_file);
599- let mut extern_path = if let Some(id) = id {
600- std::ffi::OsString::from( format!("--extern=doctest_bundle_id_{id}=") )
587+ let base_name = if is_compile_fail {
588+ format!("rust_out" )
601589 } else {
602- std::ffi::OsString::from(format!(
603- "--extern=doctest_bundle_{edition}=",
604- edition = doctest.edition
605- ))
590+ format!("doctest_bundle_{edition}", edition = doctest.edition)
606591 };
607592
608593 // Deduplicate passed -L directory paths, since usually all dependencies will be in the
@@ -622,36 +607,58 @@ fn compile_merged_doctest_and_caller_binary(
622607 }
623608 }
624609 }
625- let filename = if let Some(id) = id {
626- format!("libdoctest_bundle_id_{id}.rlib")
627- } else {
628- format!("libdoctest_bundle_{edition}.rlib", edition = doctest.edition)
629- };
630- let output_bundle_file = doctest.test_opts.outdir.path().join(filename);
610+ let output_bundle_file = doctest.test_opts.outdir.path().join(format!("lib{base_name}.rlib"));
611+ let mut extern_path = std::ffi::OsString::from(format!("--extern={base_name}="));
631612 extern_path.push(&output_bundle_file);
632- runner_compiler.arg(extern_path);
633- runner_compiler.arg(&runner_input_file);
634- if std::fs::write(&runner_input_file, test_code).is_err() {
635- // If we cannot write this file for any reason, we leave. All combined tests will be
636- // tested as standalone tests.
637- return Err((instant.elapsed(), Err(RustdocResult::CompileError)));
638- }
639- if !rustdoc_options.nocapture {
640- // If `nocapture` is disabled, then we don't display rustc's output when compiling
641- // the merged doctests.
642- runner_compiler.stderr(Stdio::null());
613+ runner_compiler.arg(&extern_path);
614+
615+ if is_compile_fail {
616+ add_rustdoc_env_vars(&mut runner_compiler, doctest);
617+ runner_compiler.stderr(Stdio::piped());
618+ runner_compiler.stdin(Stdio::piped());
619+ runner_compiler.arg("-");
620+ } else {
621+ // The actual test runner is a separate component, built with nightly-only features;
622+ // build it now
623+ let runner_input_file = doctest.path_for_merged_doctest_runner();
624+ runner_compiler.arg(&runner_input_file);
625+ if std::fs::write(&runner_input_file, test_code).is_err() {
626+ // If we cannot write this file for any reason, we leave. All combined tests will be
627+ // tested as standalone tests.
628+ return Err((instant.elapsed(), Err(RustdocResult::CompileError)));
629+ }
630+ if !rustdoc_options.nocapture {
631+ // If `nocapture` is disabled, then we don't display rustc's output when compiling
632+ // the merged doctests.
633+ runner_compiler.stderr(Stdio::null());
634+ runner_compiler.arg("--error-format=short");
635+ }
643636 }
644- runner_compiler.arg("--error-format=short");
645637 debug!("compiler invocation for doctest runner: {runner_compiler:?}");
646638
647- let status = if !status.success() {
648- status
639+ let output = if !output. status.success() {
640+ output
649641 } else {
650642 let mut child_runner = runner_compiler.spawn().expect("Failed to spawn rustc process");
651- child_runner.wait().expect("Failed to wait")
643+ if is_compile_fail {
644+ let stdin = child_runner.stdin.as_mut().expect("Failed to open stdin");
645+ stdin.write_all(test_code.as_bytes()).expect("could write out test sources");
646+ }
647+ child_runner.wait_with_output().expect("Failed to wait")
652648 };
649+ if is_compile_fail {
650+ Ok(output)
651+ } else {
652+ Ok(process::Output { status: output.status, stdout: Vec::new(), stderr: Vec::new() })
653+ }
654+ }
653655
654- Ok(process::Output { status, stdout: Vec::new(), stderr: Vec::new() })
656+ fn add_rustdoc_env_vars(compiler: &mut Command, doctest: &RunnableDocTest) {
657+ compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path);
658+ compiler.env(
659+ "UNSTABLE_RUSTDOC_TEST_LINE",
660+ format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize),
661+ );
655662}
656663
657664/// Execute a `RunnableDoctest`.
@@ -665,7 +672,6 @@ fn run_test(
665672 rustdoc_options: &RustdocOptions,
666673 supports_color: bool,
667674 report_unused_externs: impl Fn(UnusedExterns),
668- doctest_id: usize,
669675) -> (Duration, Result<(), RustdocResult>) {
670676 let langstr = &doctest.langstr;
671677 // Make sure we emit well-formed executable names for our target.
@@ -696,11 +702,6 @@ fn run_test(
696702 compiler_args.extend_from_slice(&["-Z".to_owned(), "unstable-options".to_owned()]);
697703 }
698704
699- if doctest.no_run && !langstr.compile_fail && rustdoc_options.persist_doctests.is_none() {
700- // FIXME: why does this code check if it *shouldn't* persist doctests
701- // -- shouldn't it be the negation?
702- compiler_args.push("--emit=metadata".to_owned());
703- }
704705 compiler_args.extend_from_slice(&[
705706 "--target".to_owned(),
706707 match &rustdoc_options.target {
@@ -746,40 +747,47 @@ fn run_test(
746747
747748 compiler.args(&compiler_args);
748749
749- compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path);
750- compiler.env(
751- "UNSTABLE_RUSTDOC_TEST_LINE",
752- format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize),
753- );
750+ let is_should_panic = !langstr.compile_fail && langstr.should_panic;
754751 // If this is a merged doctest, we need to write it into a file instead of using stdin
755752 // because if the size of the merged doctests is too big, it'll simply break stdin.
756- if doctest.is_multiple_tests() || (!langstr.compile_fail && langstr.should_panic) {
757- // It makes the compilation failure much faster if it is for a combined doctest.
758- compiler.arg("--error-format=short");
759- let input_file = doctest.path_for_merged_doctest_bundle(
760- if !langstr.compile_fail && langstr.should_panic { Some(doctest_id) } else { None },
761- );
762- if std::fs::write(&input_file, &doctest.full_test_code).is_err() {
763- // If we cannot write this file for any reason, we leave. All combined tests will be
764- // tested as standalone tests.
765- return (Duration::default(), Err(RustdocResult::CompileError));
766- }
767- if !rustdoc_options.nocapture {
768- // If `nocapture` is disabled, then we don't display rustc's output when compiling
769- // the merged doctests.
770- compiler.stderr(Stdio::null());
771- }
753+ if doctest.is_multiple_tests() || is_should_panic {
772754 // bundled tests are an rlib, loaded by a separate runner executable
773- compiler
774- .arg("--crate-type=lib")
775- .arg("--out-dir")
776- .arg(doctest.test_opts.outdir.path())
777- .arg(input_file);
755+ compiler.arg("--crate-type=lib").arg("--out-dir").arg(doctest.test_opts.outdir.path());
756+
757+ if !is_should_panic {
758+ compiler.arg("--error-format=short");
759+ if !rustdoc_options.nocapture {
760+ // If `nocapture` is disabled, then we don't display rustc's output when compiling
761+ // the merged doctests.
762+ compiler.stderr(Stdio::null());
763+ compiler.stdout(Stdio::null());
764+ }
765+ // It makes the compilation failure much faster if it is for a combined doctest.
766+ let input_file = doctest.path_for_merged_doctest_bundle();
767+ if std::fs::write(&input_file, &doctest.full_test_code).is_err() {
768+ // If we cannot write this file for any reason, we leave. All combined tests will be
769+ // tested as standalone tests.
770+ return (Duration::default(), Err(RustdocResult::CompileError));
771+ }
772+ compiler.arg(input_file);
773+ } else {
774+ compiler.stdin(Stdio::piped());
775+ compiler.stderr(Stdio::piped());
776+ add_rustdoc_env_vars(&mut compiler, &doctest);
777+ compiler.arg("-");
778+ }
778779 } else {
780+ add_rustdoc_env_vars(&mut compiler, &doctest);
779781 compiler.arg("--crate-type=bin").arg("-o").arg(&output_file);
780782 compiler.arg("-");
781783 compiler.stdin(Stdio::piped());
782784 compiler.stderr(Stdio::piped());
785+
786+ if doctest.no_run && !langstr.compile_fail && rustdoc_options.persist_doctests.is_none() {
787+ // FIXME: why does this code check if it *shouldn't* persist doctests
788+ // -- shouldn't it be the negation?
789+ compiler_args.push("--emit=metadata".to_owned());
790+ }
783791 }
784792
785793 debug!("compiler invocation for doctest: {compiler:?}");
@@ -795,45 +803,48 @@ fn run_test(
795803 compiler_args,
796804 merged_test_code,
797805 instant,
798- None ,
806+ false ,
799807 ) {
800808 Ok(out) => out,
801809 Err(err) => return err,
802810 }
803- } else if !langstr.compile_fail && langstr.should_panic {
804- match compile_merged_doctest_and_caller_binary(
805- child,
806- &doctest,
807- rustdoc_options,
808- rustc_binary,
809- &output_file,
810- compiler_args,
811- &format!(
812- "\
811+ } else {
812+ let stdin = child.stdin.as_mut().expect("Failed to open stdin");
813+ stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources");
814+
815+ if !langstr.compile_fail && langstr.should_panic {
816+ match compile_merged_doctest_and_caller_binary(
817+ child,
818+ &doctest,
819+ rustdoc_options,
820+ rustc_binary,
821+ &output_file,
822+ compiler_args,
823+ &format!(
824+ "\
813825#![feature(test)]
814826extern crate test;
815827
816- use std::process::{{ExitCode, Termination}};
828+ use std::process::{{ExitCode, Termination, exit }};
817829
818830fn main() -> ExitCode {{
819831 if test::cannot_handle_should_panic() {{
820- ExitCode::SUCCESS
832+ exit(test::ERROR_EXIT_CODE);
821833 }} else {{
822- extern crate doctest_bundle_id_{doctest_id} as doctest_bundle;
834+ extern crate rust_out as doctest_bundle;
823835 doctest_bundle::main().report()
824836 }}
825- }}"
826- ),
827- instant,
828- Some(doctest_id),
829- ) {
830- Ok(out) => out,
831- Err(err) => return err,
837+ }}",
838+ ),
839+ instant,
840+ true,
841+ ) {
842+ Ok(out) => out,
843+ Err(err) => return err,
844+ }
845+ } else {
846+ child.wait_with_output().expect("Failed to read stdout")
832847 }
833- } else {
834- let stdin = child.stdin.as_mut().expect("Failed to open stdin");
835- stdin.write_all(doctest.full_test_code.as_bytes()).expect("could write out test sources");
836- child.wait_with_output().expect("Failed to read stdout")
837848 };
838849
839850 struct Bomb<'a>(&'a str);
@@ -1142,7 +1153,6 @@ impl CreateRunnableDocTests {
11421153 self.opts.clone(),
11431154 Arc::clone(&self.rustdoc_options),
11441155 self.unused_extern_reports.clone(),
1145- self.standalone_tests.len(),
11461156 )
11471157 }
11481158}
@@ -1153,7 +1163,6 @@ fn generate_test_desc_and_fn(
11531163 opts: GlobalTestOptions,
11541164 rustdoc_options: Arc<RustdocOptions>,
11551165 unused_externs: Arc<Mutex<Vec<UnusedExterns>>>,
1156- doctest_id: usize,
11571166) -> test::TestDescAndFn {
11581167 let target_str = rustdoc_options.target.to_string();
11591168 let rustdoc_test_options =
@@ -1188,7 +1197,6 @@ fn generate_test_desc_and_fn(
11881197 scraped_test,
11891198 rustdoc_options,
11901199 unused_externs,
1191- doctest_id,
11921200 )
11931201 })),
11941202 }
@@ -1201,7 +1209,6 @@ fn doctest_run_fn(
12011209 scraped_test: ScrapedDocTest,
12021210 rustdoc_options: Arc<RustdocOptions>,
12031211 unused_externs: Arc<Mutex<Vec<UnusedExterns>>>,
1204- doctest_id: usize,
12051212) -> Result<(), String> {
12061213 #[cfg(not(bootstrap))]
12071214 if scraped_test.langstr.should_panic && test::cannot_handle_should_panic() {
@@ -1227,13 +1234,8 @@ fn doctest_run_fn(
12271234 no_run: scraped_test.no_run(&rustdoc_options),
12281235 merged_test_code: None,
12291236 };
1230- let (_, res) = run_test(
1231- runnable_test,
1232- &rustdoc_options,
1233- doctest.supports_color,
1234- report_unused_externs,
1235- doctest_id,
1236- );
1237+ let (_, res) =
1238+ run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs);
12371239
12381240 if let Err(err) = res {
12391241 eprint!("{err}");
0 commit comments