@@ -318,6 +318,7 @@ enum TestFailure {
318
318
UnexpectedRunPass ,
319
319
}
320
320
321
+ #[ derive( Debug ) ]
321
322
enum DirState {
322
323
Temp ( tempfile:: TempDir ) ,
323
324
Perm ( PathBuf ) ,
@@ -372,21 +373,41 @@ pub(crate) struct DocTestInfo {
372
373
path : PathBuf ,
373
374
}
374
375
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
+
375
394
fn run_test (
376
395
test : String ,
377
396
supports_color : bool ,
378
397
test_info : Option < DocTestInfo > ,
379
398
rustdoc_options : Arc < IndividualTestOptions > ,
380
399
is_multiple_tests : bool ,
381
- outdir : Arc < DirState > ,
382
400
mut lang_string : LangString ,
383
401
edition : Edition ,
384
402
report_unused_externs : impl Fn ( UnusedExterns ) ,
385
403
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 ,
386
407
) -> 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) ;
390
411
391
412
let rustc_binary = rustdoc_options
392
413
. test_builder
@@ -722,6 +743,7 @@ impl DocTest {
722
743
let Self {
723
744
supports_color, rustdoc_test_options, lang_string, outdir, path, no_run, ..
724
745
} = self ;
746
+ let out_dir = build_test_dir ( & outdir, false ) ;
725
747
TestDescAndFn {
726
748
desc : test:: TestDesc {
727
749
name : test:: DynTestName ( std:: mem:: replace ( & mut self . name , String :: new ( ) ) ) ,
@@ -748,12 +770,18 @@ impl DocTest {
748
770
Some ( DocTestInfo { line_offset, line : self . line , path } ) ,
749
771
rustdoc_test_options,
750
772
false ,
751
- outdir,
752
773
lang_string,
753
774
edition,
754
775
report_unused_externs,
755
776
no_run,
777
+ None ,
778
+ out_dir,
756
779
) ;
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) ;
757
785
758
786
if let Err ( err) = res {
759
787
match err {
@@ -897,11 +925,6 @@ pub(crate) fn make_test(
897
925
let mut path = path. clone ( ) ;
898
926
path. push ( & test_id) ;
899
927
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
-
905
928
DirState :: Perm ( path)
906
929
} else {
907
930
DirState :: Temp ( get_doctest_dir ( ) . expect ( "rustdoc needs a tempdir" ) )
@@ -1379,17 +1402,20 @@ test::test_main(&[{test_args}], vec![{ids}], None);
1379
1402
ids = self . ids,
1380
1403
)
1381
1404
. expect ( "failed to generate test code" ) ;
1405
+ let out_dir = build_test_dir ( self . outdir , true ) ;
1382
1406
let ret = run_test (
1383
1407
code,
1384
1408
self . supports_color ,
1385
1409
None ,
1386
1410
Arc :: clone ( self . rustdoc_test_options ) ,
1387
1411
true ,
1388
- Arc :: clone ( self . outdir ) ,
1389
1412
LangString :: empty_for_test ( ) ,
1390
1413
self . edition ,
1391
1414
|_: UnusedExterns | { } ,
1392
1415
false ,
1416
+ // To prevent writing over an existing doctest
1417
+ Some ( & format ! ( "_{}_{}" , self . edition, * self . ran_edition_tests) ) ,
1418
+ out_dir,
1393
1419
) ;
1394
1420
if let Err ( TestFailure :: CompileError ) = ret {
1395
1421
// We failed to compile all compatible tests as one so we push them into the
0 commit comments