diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index f5ea54971b848..d8064cec13b96 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -548,6 +548,8 @@ fn wrapped_rustc_command(rustc_wrappers: &[PathBuf], rustc_binary: &Path) -> Com /// and everything needed to calculate the compiler's command-line arguments. /// The `# ` prefix on boring lines has also been stripped. pub(crate) struct RunnableDocTest { + /// In a merged test, this is the code for the "bundle" that contains the actual doctests. + /// In a standalone test this is just the regular test code. full_test_code: String, full_test_line_offset: usize, test_opts: IndividualTestOptions, @@ -556,7 +558,9 @@ pub(crate) struct RunnableDocTest { line: usize, edition: Edition, no_run: bool, - merged_test_code: Option, + /// If `Some`, this is a merged test and the string is the code for the "runner" that contains + /// the test harness to invoke the doctests. + merged_test_runner_code: Option, } impl RunnableDocTest { @@ -567,7 +571,7 @@ impl RunnableDocTest { self.test_opts.outdir.path().join(format!("doctest_runner_{}.rs", self.edition)) } fn is_multiple_tests(&self) -> bool { - self.merged_test_code.is_some() + self.merged_test_runner_code.is_some() } } @@ -706,7 +710,7 @@ fn run_test( return (Duration::default(), Err(TestFailure::CompileError)); } }; - let output = if let Some(merged_test_code) = &doctest.merged_test_code { + let output = if let Some(merged_test_runner_code) = &doctest.merged_test_runner_code { // compile-fail tests never get merged, so this should always pass let status = child.wait().expect("Failed to wait"); @@ -751,7 +755,7 @@ fn run_test( extern_path.push(&output_bundle_file); runner_compiler.arg(extern_path); runner_compiler.arg(&runner_input_file); - if std::fs::write(&runner_input_file, merged_test_code).is_err() { + if std::fs::write(&runner_input_file, merged_test_runner_code).is_err() { // If we cannot write this file for any reason, we leave. All combined tests will be // tested as standalone tests. return (instant.elapsed(), Err(TestFailure::CompileError)); @@ -1180,7 +1184,7 @@ fn doctest_run_fn( line: scraped_test.line, edition: scraped_test.edition(&rustdoc_options), no_run: scraped_test.no_run(&rustdoc_options), - merged_test_code: None, + merged_test_runner_code: None, }; let (_, res) = run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs); diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index ac82829fa662e..1fe62015b2c55 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -33,8 +33,11 @@ struct ParseSourceInfo { has_macro_def: bool, everything_else: String, crates: String, + /// Inner attributes (`#![...]`) from the source that have to be put at the crate level. crate_attrs: String, - maybe_crate_attrs: String, + /// Inner attributes (`#![...]`) from the source that can be put into a module and therefore do + /// not inhibit merging: even in the merged test, the attributes can be isolated to the test. + module_attrs: String, } /// Builder type for `DocTestBuilder`. @@ -143,7 +146,7 @@ impl<'a> BuildDocTestBuilder<'a> { everything_else, crates, crate_attrs, - maybe_crate_attrs, + module_attrs, })) = result else { // If the AST returned an error, we don't want this doctest to be merged with the @@ -158,7 +161,7 @@ impl<'a> BuildDocTestBuilder<'a> { ); }; - debug!("crate_attrs:\n{crate_attrs}{maybe_crate_attrs}"); + debug!("crate_attrs:\n{crate_attrs}{module_attrs}"); debug!("crates:\n{crates}"); debug!("after:\n{everything_else}"); debug!("merge-doctests: {can_merge_doctests:?}"); @@ -187,7 +190,7 @@ impl<'a> BuildDocTestBuilder<'a> { has_main_fn, global_crate_attrs, crate_attrs, - maybe_crate_attrs, + module_attrs, crates, everything_else, already_has_extern_crate, @@ -208,7 +211,7 @@ pub(crate) struct DocTestBuilder { pub(crate) crate_attrs: String, /// If this is a merged doctest, it will be put into `everything_else`, otherwise it will /// put into `crate_attrs`. - pub(crate) maybe_crate_attrs: String, + pub(crate) module_attrs: String, pub(crate) crates: String, pub(crate) everything_else: String, pub(crate) test_id: Option, @@ -294,7 +297,7 @@ impl DocTestBuilder { fn invalid( global_crate_attrs: Vec, crate_attrs: String, - maybe_crate_attrs: String, + module_attrs: String, crates: String, everything_else: String, test_id: Option, @@ -304,7 +307,7 @@ impl DocTestBuilder { has_main_fn: false, global_crate_attrs, crate_attrs, - maybe_crate_attrs, + module_attrs, crates, everything_else, already_has_extern_crate: false, @@ -347,17 +350,16 @@ impl DocTestBuilder { line_offset += 1; } - // Now push any outer attributes from the example, assuming they - // are intended to be crate attributes. + // Now push any outer attributes from the example (both crate and module attributes). if !self.crate_attrs.is_empty() { crate_level_code.push_str(&self.crate_attrs); if !self.crate_attrs.ends_with('\n') { crate_level_code.push('\n'); } } - if !self.maybe_crate_attrs.is_empty() { - crate_level_code.push_str(&self.maybe_crate_attrs); - if !self.maybe_crate_attrs.ends_with('\n') { + if !self.module_attrs.is_empty() { + crate_level_code.push_str(&self.module_attrs); + if !self.module_attrs.ends_with('\n') { crate_level_code.push('\n'); } } @@ -590,12 +592,7 @@ fn parse_source( { push_to_s(&mut info.crate_attrs, source, attr.span, &mut prev_span_hi); } else { - push_to_s( - &mut info.maybe_crate_attrs, - source, - attr.span, - &mut prev_span_hi, - ); + push_to_s(&mut info.module_attrs, source, attr.span, &mut prev_span_hi); } } else { push_to_s(&mut info.crate_attrs, source, attr.span, &mut prev_span_hi); @@ -657,7 +654,7 @@ fn parse_source( span = span.with_lo(attr.span.lo()); } if info.everything_else.is_empty() - && (!info.maybe_crate_attrs.is_empty() || !info.crate_attrs.is_empty()) + && (!info.module_attrs.is_empty() || !info.crate_attrs.is_empty()) { // To keep the doctest code "as close as possible" to the original, we insert // all the code located between this new span and the previous span which diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index fcfa424968e48..ff9397ea2460b 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -80,7 +80,7 @@ impl DocTestRunner { test_args: &[String], rustdoc_options: &RustdocOptions, ) -> (Duration, Result) { - let mut code = "\ + let mut runner_code = "\ #![allow(unused_extern_crates)] #![allow(internal_features)] #![feature(test)] @@ -108,15 +108,15 @@ impl DocTestRunner { code_prefix.push_str(&format!("#![{attr}]\n")); } - code.push_str("extern crate test;\n"); - writeln!(code, "extern crate doctest_bundle_{edition} as doctest_bundle;").unwrap(); + runner_code.push_str("extern crate test;\n"); + writeln!(runner_code, "extern crate doctest_bundle_{edition} as doctest_bundle;").unwrap(); let test_args = test_args.iter().fold(String::new(), |mut x, arg| { write!(x, "{arg:?}.to_string(),").unwrap(); x }); write!( - code, + runner_code, "\ {output} @@ -207,7 +207,7 @@ std::process::Termination::report(test::test_main(test_args, tests, None)) line: 0, edition, no_run: false, - merged_test_code: Some(code), + merged_test_runner_code: Some(runner_code), }; let (duration, ret) = run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {}); @@ -230,7 +230,7 @@ fn generate_mergeable_doctest( // We generate nothing else. writeln!(output, "pub mod {test_id} {{}}\n").unwrap(); } else { - writeln!(output, "pub mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs) + writeln!(output, "pub mod {test_id} {{\n{}{}", doctest.crates, doctest.module_attrs) .unwrap(); if doctest.has_main_fn { output.push_str(&doctest.everything_else);