@@ -318,6 +318,7 @@ enum TestFailure {
318318 UnexpectedRunPass ,
319319}
320320
321+ #[ derive( Debug ) ]
321322enum DirState {
322323 Temp ( tempfile:: TempDir ) ,
323324 Perm ( PathBuf ) ,
@@ -372,21 +373,41 @@ pub(crate) struct DocTestInfo {
372373 path : PathBuf ,
373374}
374375
376+ fn build_test_dir ( outdir : & Arc < DirState > , is_multiple_tests : bool ) -> PathBuf {
377+ // Make sure we emit well-formed executable names for our target.
378+ let is_perm_dir = matches ! ( * * outdir, DirState :: Perm ( ..) ) ;
379+ let out_dir = outdir. path ( ) ;
380+ let out_dir = if is_multiple_tests && is_perm_dir {
381+ // If this a "multiple tests" case and we generate it into a non temporary directory, we
382+ // want to put it into the parent instead.
383+ out_dir. parent ( ) . unwrap_or ( out_dir)
384+ } else {
385+ out_dir
386+ } ;
387+ if is_perm_dir && let Err ( err) = std:: fs:: create_dir_all ( & out_dir) {
388+ eprintln ! ( "Couldn't create directory for doctest executables: {err}" ) ;
389+ panic:: resume_unwind ( Box :: new ( ( ) ) ) ;
390+ }
391+ out_dir. into ( )
392+ }
393+
375394fn run_test (
376395 test : String ,
377396 supports_color : bool ,
378397 test_info : Option < DocTestInfo > ,
379398 rustdoc_options : Arc < IndividualTestOptions > ,
380399 is_multiple_tests : bool ,
381- outdir : Arc < DirState > ,
382400 mut lang_string : LangString ,
383401 edition : Edition ,
384402 report_unused_externs : impl Fn ( UnusedExterns ) ,
385403 no_run : bool ,
404+ // Used to prevent overwriting a binary in case `--persist-doctests` is used.
405+ binary_extra : Option < & str > ,
406+ out_dir : PathBuf ,
386407) -> Result < ( ) , TestFailure > {
387- // Make sure we emit well-formed executable names for our target.
388- let rust_out = add_exe_suffix ( "rust_out" . to_owned ( ) , & rustdoc_options. target ) ;
389- let output_file = outdir . path ( ) . join ( rust_out) ;
408+ let rust_out =
409+ add_exe_suffix ( format ! ( "rust_out{}" , binary_extra . unwrap_or ( "" ) ) , & rustdoc_options. target ) ;
410+ let output_file = out_dir . join ( rust_out) ;
390411
391412 let rustc_binary = rustdoc_options
392413 . test_builder
@@ -722,6 +743,7 @@ impl DocTest {
722743 let Self {
723744 supports_color, rustdoc_test_options, lang_string, outdir, path, no_run, ..
724745 } = self ;
746+ let out_dir = build_test_dir ( & outdir, false ) ;
725747 TestDescAndFn {
726748 desc : test:: TestDesc {
727749 name : test:: DynTestName ( std:: mem:: replace ( & mut self . name , String :: new ( ) ) ) ,
@@ -748,12 +770,18 @@ impl DocTest {
748770 Some ( DocTestInfo { line_offset, line : self . line , path } ) ,
749771 rustdoc_test_options,
750772 false ,
751- outdir,
752773 lang_string,
753774 edition,
754775 report_unused_externs,
755776 no_run,
777+ None ,
778+ out_dir,
756779 ) ;
780+ // We need to move `outdir` into the closure to ensure the `TempDir` struct won't
781+ // be dropped before all tests have been run.
782+ //
783+ // The call to `drop` is only to make use of `outdir`.
784+ drop ( outdir) ;
757785
758786 if let Err ( err) = res {
759787 match err {
@@ -897,11 +925,6 @@ pub(crate) fn make_test(
897925 let mut path = path. clone ( ) ;
898926 path. push ( & test_id) ;
899927
900- if let Err ( err) = std:: fs:: create_dir_all ( & path) {
901- eprintln ! ( "Couldn't create directory for doctest executables: {err}" ) ;
902- panic:: resume_unwind ( Box :: new ( ( ) ) ) ;
903- }
904-
905928 DirState :: Perm ( path)
906929 } else {
907930 DirState :: Temp ( get_doctest_dir ( ) . expect ( "rustdoc needs a tempdir" ) )
@@ -1379,17 +1402,20 @@ test::test_main(&[{test_args}], vec![{ids}], None);
13791402 ids = self . ids,
13801403 )
13811404 . expect ( "failed to generate test code" ) ;
1405+ let out_dir = build_test_dir ( self . outdir , true ) ;
13821406 let ret = run_test (
13831407 code,
13841408 self . supports_color ,
13851409 None ,
13861410 Arc :: clone ( self . rustdoc_test_options ) ,
13871411 true ,
1388- Arc :: clone ( self . outdir ) ,
13891412 LangString :: empty_for_test ( ) ,
13901413 self . edition ,
13911414 |_: UnusedExterns | { } ,
13921415 false ,
1416+ // To prevent writing over an existing doctest
1417+ Some ( & format ! ( "_{}_{}" , self . edition, * self . ran_edition_tests) ) ,
1418+ out_dir,
13931419 ) ;
13941420 if let Err ( TestFailure :: CompileError ) = ret {
13951421 // We failed to compile all compatible tests as one so we push them into the
0 commit comments