@@ -211,14 +211,15 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
211211 Err ( error) => return crate :: wrap_return ( dcx, Err ( error) ) ,
212212 } ;
213213 let args_path = temp_dir. path ( ) . join ( "rustdoc-cfgs" ) ;
214+ let temp_dir_path = temp_dir. path ( ) . to_path_buf ( ) ;
214215 crate :: wrap_return ( dcx, generate_args_file ( & args_path, & options) ) ;
215216
216217 let extract_doctests = options. output_format == OutputFormat :: Doctest ;
217218 let save_temps = options. codegen_options . save_temps ;
218219 let result = interface:: run_compiler ( config, |compiler| {
219220 let krate = rustc_interface:: passes:: parse ( & compiler. sess ) ;
220221
221- let collector = rustc_interface:: create_and_enter_global_ctxt ( compiler, krate, |tcx| {
222+ rustc_interface:: create_and_enter_global_ctxt ( compiler, krate, |tcx| {
222223 let crate_name = tcx. crate_name ( LOCAL_CRATE ) . to_string ( ) ;
223224 let opts = scrape_test_config ( tcx, crate_name, args_path) ;
224225
@@ -227,6 +228,8 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
227228 tcx,
228229 ) ;
229230 let tests = hir_collector. collect_crate ( ) ;
231+ tcx. dcx ( ) . abort_if_errors ( ) ;
232+
230233 if extract_doctests {
231234 let mut collector = extracted:: ExtractedDocTests :: new ( ) ;
232235 tests. into_iter ( ) . for_each ( |t| collector. add_test ( t, & opts, & options) ) ;
@@ -235,93 +238,90 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
235238 let mut stdout = stdout. lock ( ) ;
236239 if let Err ( error) = serde_json:: ser:: to_writer ( & mut stdout, & collector) {
237240 eprintln ! ( ) ;
238- Err ( format ! ( "Failed to generate JSON output for doctests: {error:?}" ) )
241+ return Err ( format ! ( "Failed to generate JSON output for doctests: {error:?}" ) ) ;
239242 } else {
240- Ok ( None )
243+ return Ok ( ( ) ) ;
241244 }
242- } else {
243- let mut collector = CreateRunnableDocTests :: new ( options, opts) ;
244- tests. into_iter ( ) . for_each ( |t| collector. add_test ( t, Some ( compiler. sess . dcx ( ) ) ) ) ;
245-
246- Ok ( Some ( collector) )
247245 }
248- } ) ;
249- compiler. sess . dcx ( ) . abort_if_errors ( ) ;
246+ let mut collector = CreateRunnableDocTests :: new ( options , opts ) ;
247+ tests . into_iter ( ) . for_each ( |t| collector . add_test ( t , Some ( compiler. sess . dcx ( ) ) ) ) ;
250248
251- collector
252- } ) ;
249+ let CreateRunnableDocTests {
250+ standalone_tests,
251+ mergeable_tests,
252+ rustdoc_options,
253+ opts,
254+ unused_extern_reports,
255+ compiling_test_count,
256+ ..
257+ } = collector;
253258
254- let CreateRunnableDocTests {
255- standalone_tests,
256- mergeable_tests,
257- rustdoc_options,
258- opts,
259- unused_extern_reports,
260- compiling_test_count,
261- ..
262- } = match result {
263- Ok ( Some ( collector) ) => collector,
264- Ok ( None ) => return ,
265- Err ( error) => {
266- eprintln ! ( "{error}" ) ;
267- // Since some files in the temporary folder are still owned and alive, we need
268- // to manually remove the folder.
269- if !save_temps {
270- let _ = std:: fs:: remove_dir_all ( temp_dir. path ( ) ) ;
259+ run_tests (
260+ compiler. sess . dcx ( ) ,
261+ opts,
262+ & rustdoc_options,
263+ & unused_extern_reports,
264+ standalone_tests,
265+ mergeable_tests,
266+ Some ( temp_dir) ,
267+ Some ( tcx) ,
268+ ) ;
269+
270+ let compiling_test_count = compiling_test_count. load ( Ordering :: SeqCst ) ;
271+
272+ // Collect and warn about unused externs, but only if we've gotten
273+ // reports for each doctest
274+ if json_unused_externs. is_enabled ( ) {
275+ let unused_extern_reports: Vec < _ > =
276+ std:: mem:: take ( & mut unused_extern_reports. lock ( ) . unwrap ( ) ) ;
277+ if unused_extern_reports. len ( ) == compiling_test_count {
278+ let extern_names =
279+ externs. iter ( ) . map ( |( name, _) | name) . collect :: < FxIndexSet < & String > > ( ) ;
280+ let mut unused_extern_names = unused_extern_reports
281+ . iter ( )
282+ . map ( |uexts| {
283+ uexts. unused_extern_names . iter ( ) . collect :: < FxIndexSet < & String > > ( )
284+ } )
285+ . fold ( extern_names, |uextsa, uextsb| {
286+ uextsa. intersection ( & uextsb) . copied ( ) . collect :: < FxIndexSet < & String > > ( )
287+ } )
288+ . iter ( )
289+ . map ( |v| ( * v) . clone ( ) )
290+ . collect :: < Vec < String > > ( ) ;
291+ unused_extern_names. sort ( ) ;
292+ // Take the most severe lint level
293+ let lint_level = unused_extern_reports
294+ . iter ( )
295+ . map ( |uexts| uexts. lint_level . as_str ( ) )
296+ . max_by_key ( |v| match * v {
297+ "warn" => 1 ,
298+ "deny" => 2 ,
299+ "forbid" => 3 ,
300+ // The allow lint level is not expected,
301+ // as if allow is specified, no message
302+ // is to be emitted.
303+ v => unreachable ! ( "Invalid lint level '{v}'" ) ,
304+ } )
305+ . unwrap_or ( "warn" )
306+ . to_string ( ) ;
307+ let uext = UnusedExterns { lint_level, unused_extern_names } ;
308+ let unused_extern_json = serde_json:: to_string ( & uext) . unwrap ( ) ;
309+ eprintln ! ( "{unused_extern_json}" ) ;
310+ }
271311 }
272- std:: process:: exit ( 1 ) ;
273- }
274- } ;
275312
276- run_tests (
277- dcx,
278- opts,
279- & rustdoc_options,
280- & unused_extern_reports,
281- standalone_tests,
282- mergeable_tests,
283- Some ( temp_dir) ,
284- ) ;
313+ Ok ( ( ) )
314+ } )
315+ } ) ;
285316
286- let compiling_test_count = compiling_test_count. load ( Ordering :: SeqCst ) ;
287-
288- // Collect and warn about unused externs, but only if we've gotten
289- // reports for each doctest
290- if json_unused_externs. is_enabled ( ) {
291- let unused_extern_reports: Vec < _ > =
292- std:: mem:: take ( & mut unused_extern_reports. lock ( ) . unwrap ( ) ) ;
293- if unused_extern_reports. len ( ) == compiling_test_count {
294- let extern_names =
295- externs. iter ( ) . map ( |( name, _) | name) . collect :: < FxIndexSet < & String > > ( ) ;
296- let mut unused_extern_names = unused_extern_reports
297- . iter ( )
298- . map ( |uexts| uexts. unused_extern_names . iter ( ) . collect :: < FxIndexSet < & String > > ( ) )
299- . fold ( extern_names, |uextsa, uextsb| {
300- uextsa. intersection ( & uextsb) . copied ( ) . collect :: < FxIndexSet < & String > > ( )
301- } )
302- . iter ( )
303- . map ( |v| ( * v) . clone ( ) )
304- . collect :: < Vec < String > > ( ) ;
305- unused_extern_names. sort ( ) ;
306- // Take the most severe lint level
307- let lint_level = unused_extern_reports
308- . iter ( )
309- . map ( |uexts| uexts. lint_level . as_str ( ) )
310- . max_by_key ( |v| match * v {
311- "warn" => 1 ,
312- "deny" => 2 ,
313- "forbid" => 3 ,
314- // The allow lint level is not expected,
315- // as if allow is specified, no message
316- // is to be emitted.
317- v => unreachable ! ( "Invalid lint level '{v}'" ) ,
318- } )
319- . unwrap_or ( "warn" )
320- . to_string ( ) ;
321- let uext = UnusedExterns { lint_level, unused_extern_names } ;
322- let unused_extern_json = serde_json:: to_string ( & uext) . unwrap ( ) ;
323- eprintln ! ( "{unused_extern_json}" ) ;
317+ if let Err ( error) = result {
318+ eprintln ! ( "{error}" ) ;
319+ // Since some files in the temporary folder are still owned and alive, we need
320+ // to manually remove the folder.
321+ if !save_temps {
322+ let _ = std:: fs:: remove_dir_all ( temp_dir_path) ;
324323 }
324+ std:: process:: exit ( 1 ) ;
325325 }
326326}
327327
@@ -334,6 +334,7 @@ pub(crate) fn run_tests(
334334 mergeable_tests : FxIndexMap < MergeableTestKey , Vec < ( DocTestBuilder , ScrapedDocTest ) > > ,
335335 // We pass this argument so we can drop it manually before using `exit`.
336336 mut temp_dir : Option < TempDir > ,
337+ tcx : Option < TyCtxt < ' _ > > ,
337338) {
338339 let mut test_args = Vec :: with_capacity ( rustdoc_options. test_args . len ( ) + 1 ) ;
339340 test_args. insert ( 0 , "rustdoctest" . to_string ( ) ) ;
@@ -386,6 +387,20 @@ pub(crate) fn run_tests(
386387 diag. emit ( ) ;
387388 }
388389
390+ if let Some ( tcx) = tcx {
391+ tcx. node_span_lint (
392+ crate :: lint:: FAILED_MERGED_DOCTEST_COMPILATION ,
393+ CRATE_HIR_ID ,
394+ tcx. hir_span ( CRATE_HIR_ID ) ,
395+ |lint| {
396+ lint. primary_message ( format ! (
397+ "failed to compile merged doctests for edition {edition}. \
398+ Reverting to standalone doctests."
399+ ) ) ;
400+ } ,
401+ ) ;
402+ }
403+
389404 // We failed to compile all compatible tests as one so we push them into the
390405 // `standalone_tests` doctests.
391406 debug ! ( "Failed to compile compatible doctests for edition {} all at once" , edition) ;
0 commit comments