diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 56ccdfae1d8bc..ac24543929b66 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -29,20 +29,21 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use crate::clean::{types::AttributesExt, Attributes}; -use crate::config::Options; +use crate::config::Options as RustdocOptions; use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; use crate::lint::init_lints; use crate::passes::span_of_attrs; +/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`). #[derive(Clone, Default)] -crate struct TestOptions { +crate struct GlobalTestOptions { /// Whether to disable the default `extern crate my_crate;` when creating doctests. crate no_crate_inject: bool, /// Additional crate-level attributes to add to doctests. crate attrs: Vec, } -crate fn run(options: Options) -> Result<(), ErrorReported> { +crate fn run(options: RustdocOptions) -> Result<(), ErrorReported> { let input = config::Input::File(options.input.clone()); let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; @@ -214,10 +215,10 @@ crate fn run_tests(mut test_args: Vec, nocapture: bool, tests: Vec TestOptions { +fn scrape_test_config(attrs: &[ast::Attribute]) -> GlobalTestOptions { use rustc_ast_pretty::pprust; - let mut opts = TestOptions { no_crate_inject: false, attrs: Vec::new() }; + let mut opts = GlobalTestOptions { no_crate_inject: false, attrs: Vec::new() }; let test_attrs: Vec<_> = attrs .iter() @@ -292,16 +293,13 @@ fn run_test( test: &str, crate_name: &str, line: usize, - options: Options, - should_panic: bool, + rustdoc_options: RustdocOptions, + mut lang_string: LangString, no_run: bool, - as_test_harness: bool, runtool: Option, runtool_args: Vec, target: TargetTriple, - compile_fail: bool, - mut error_codes: Vec, - opts: &TestOptions, + opts: &GlobalTestOptions, edition: Edition, outdir: DirState, path: PathBuf, @@ -309,49 +307,49 @@ fn run_test( report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { let (test, line_offset, supports_color) = - make_test(test, Some(crate_name), as_test_harness, opts, edition, Some(test_id)); + make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id)); let output_file = outdir.path().join("rust_out"); - let rustc_binary = options + let rustc_binary = rustdoc_options .test_builder .as_deref() .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = Command::new(&rustc_binary); compiler.arg("--crate-type").arg("bin"); - for cfg in &options.cfgs { + for cfg in &rustdoc_options.cfgs { compiler.arg("--cfg").arg(&cfg); } - if let Some(sysroot) = options.maybe_sysroot { + if let Some(sysroot) = rustdoc_options.maybe_sysroot { compiler.arg("--sysroot").arg(sysroot); } compiler.arg("--edition").arg(&edition.to_string()); compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); compiler.arg("-o").arg(&output_file); - if as_test_harness { + if lang_string.test_harness { compiler.arg("--test"); } - if options.json_unused_externs && !compile_fail { + if rustdoc_options.json_unused_externs && !lang_string.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); compiler.arg("-Z").arg("unstable-options"); compiler.arg("-W").arg("unused_crate_dependencies"); } - for lib_str in &options.lib_strs { + for lib_str in &rustdoc_options.lib_strs { compiler.arg("-L").arg(&lib_str); } - for extern_str in &options.extern_strs { + for extern_str in &rustdoc_options.extern_strs { compiler.arg("--extern").arg(&extern_str); } compiler.arg("-Ccodegen-units=1"); - for codegen_options_str in &options.codegen_options_strs { + for codegen_options_str in &rustdoc_options.codegen_options_strs { compiler.arg("-C").arg(&codegen_options_str); } - for debugging_option_str in &options.debugging_opts_strs { + for debugging_option_str in &rustdoc_options.debugging_opts_strs { compiler.arg("-Z").arg(&debugging_option_str); } - if no_run && !compile_fail && options.persist_doctests.is_none() { + if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() { compiler.arg("--emit=metadata"); } compiler.arg("--target").arg(match target { @@ -360,7 +358,7 @@ fn run_test( path.to_str().expect("target path must be valid unicode").to_string() } }); - if let ErrorOutputType::HumanReadable(kind) = options.error_format { + if let ErrorOutputType::HumanReadable(kind) = rustdoc_options.error_format { let (short, color_config) = kind.unzip(); if short { @@ -418,20 +416,20 @@ fn run_test( let out = out_lines.join("\n"); let _bomb = Bomb(&out); - match (output.status.success(), compile_fail) { + match (output.status.success(), lang_string.compile_fail) { (true, true) => { return Err(TestFailure::UnexpectedCompilePass); } (true, false) => {} (false, true) => { - if !error_codes.is_empty() { + if !lang_string.error_codes.is_empty() { // We used to check if the output contained "error[{}]: " but since we added the // colored output, we can't anymore because of the color escape characters before // the ":". - error_codes.retain(|err| !out.contains(&format!("error[{}]", err))); + lang_string.error_codes.retain(|err| !out.contains(&format!("error[{}]", err))); - if !error_codes.is_empty() { - return Err(TestFailure::MissingErrorCodes(error_codes)); + if !lang_string.error_codes.is_empty() { + return Err(TestFailure::MissingErrorCodes(lang_string.error_codes)); } } } @@ -454,11 +452,11 @@ fn run_test( } else { cmd = Command::new(output_file); } - if let Some(run_directory) = options.test_run_directory { + if let Some(run_directory) = rustdoc_options.test_run_directory { cmd.current_dir(run_directory); } - let result = if options.nocapture { + let result = if rustdoc_options.nocapture { cmd.status().map(|status| process::Output { status, stdout: Vec::new(), @@ -470,9 +468,9 @@ fn run_test( match result { Err(e) => return Err(TestFailure::ExecutionError(e)), Ok(out) => { - if should_panic && out.status.success() { + if lang_string.should_panic && out.status.success() { return Err(TestFailure::UnexpectedRunPass); - } else if !should_panic && !out.status.success() { + } else if !lang_string.should_panic && !out.status.success() { return Err(TestFailure::ExecutionFailure(out)); } } @@ -487,7 +485,7 @@ crate fn make_test( s: &str, crate_name: Option<&str>, dont_insert_main: bool, - opts: &TestOptions, + opts: &GlobalTestOptions, edition: Edition, test_id: Option<&str>, ) -> (String, usize, bool) { @@ -804,11 +802,11 @@ crate struct Collector { // the `names` vector of that test will be `["Title", "Subtitle"]`. names: Vec, - options: Options, + rustdoc_options: RustdocOptions, use_headers: bool, enable_per_target_ignores: bool, crate_name: Symbol, - opts: TestOptions, + opts: GlobalTestOptions, position: Span, source_map: Option>, filename: Option, @@ -820,9 +818,9 @@ crate struct Collector { impl Collector { crate fn new( crate_name: Symbol, - options: Options, + rustdoc_options: RustdocOptions, use_headers: bool, - opts: TestOptions, + opts: GlobalTestOptions, source_map: Option>, filename: Option, enable_per_target_ignores: bool, @@ -830,7 +828,7 @@ impl Collector { Collector { tests: Vec::new(), names: Vec::new(), - options, + rustdoc_options, use_headers, enable_per_target_ignores, crate_name, @@ -884,14 +882,14 @@ impl Tester for Collector { let name = self.generate_name(line, &filename); let crate_name = self.crate_name.to_string(); let opts = self.opts.clone(); - let edition = config.edition.unwrap_or(self.options.edition); - let options = self.options.clone(); - let runtool = self.options.runtool.clone(); - let runtool_args = self.options.runtool_args.clone(); - let target = self.options.target.clone(); + let edition = config.edition.unwrap_or(self.rustdoc_options.edition); + let rustdoc_options = self.rustdoc_options.clone(); + let runtool = self.rustdoc_options.runtool.clone(); + let runtool_args = self.rustdoc_options.runtool_args.clone(); + let target = self.rustdoc_options.target.clone(); let target_str = target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = config.no_run || options.no_run; + let no_run = config.no_run || rustdoc_options.no_run; if !config.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } @@ -925,7 +923,7 @@ impl Tester for Collector { self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0) }, ); - let outdir = if let Some(mut path) = options.persist_doctests.clone() { + let outdir = if let Some(mut path) = rustdoc_options.persist_doctests.clone() { path.push(&test_id); std::fs::create_dir_all(&path) @@ -965,15 +963,12 @@ impl Tester for Collector { &test, &crate_name, line, - options, - config.should_panic, + rustdoc_options, + config, no_run, - config.test_harness, runtool, runtool_args, target, - config.compile_fail, - config.error_codes, &opts, edition, outdir, diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs index 099609d0f912e..360d2259ea3d2 100644 --- a/src/librustdoc/doctest/tests.rs +++ b/src/librustdoc/doctest/tests.rs @@ -1,10 +1,10 @@ -use super::{make_test, TestOptions}; +use super::{make_test, GlobalTestOptions}; use rustc_span::edition::DEFAULT_EDITION; #[test] fn make_test_basic() { //basic use: wraps with `fn main`, adds `#![allow(unused)]` - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { @@ -19,7 +19,7 @@ assert_eq!(2+2, 4); fn make_test_crate_name_no_use() { // If you give a crate name but *don't* use it within the test, it won't bother inserting // the `extern crate` statement. - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { @@ -34,7 +34,7 @@ assert_eq!(2+2, 4); fn make_test_crate_name() { // If you give a crate name and use it within the test, it will insert an `extern crate` // statement before `fn main`. - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -52,7 +52,7 @@ assert_eq!(2+2, 4); fn make_test_no_crate_inject() { // Even if you do use the crate within the test, setting `opts.no_crate_inject` will skip // adding it anyway. - let opts = TestOptions { no_crate_inject: true, attrs: vec![] }; + let opts = GlobalTestOptions { no_crate_inject: true, attrs: vec![] }; let input = "use asdf::qwop; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -70,7 +70,7 @@ fn make_test_ignore_std() { // Even if you include a crate name, and use it in the doctest, we still won't include an // `extern crate` statement if the crate is "std" -- that's included already by the // compiler! - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "use std::*; assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -87,7 +87,7 @@ assert_eq!(2+2, 4); fn make_test_manual_extern_crate() { // When you manually include an `extern crate` statement in your doctest, `make_test` // assumes you've included one for your own crate too. - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "extern crate asdf; use asdf::qwop; assert_eq!(2+2, 4);"; @@ -104,7 +104,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_manual_extern_crate_with_macro_use() { - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "#[macro_use] extern crate asdf; use asdf::qwop; assert_eq!(2+2, 4);"; @@ -123,7 +123,7 @@ assert_eq!(2+2, 4); fn make_test_opts_attrs() { // If you supplied some doctest attributes with `#![doc(test(attr(...)))]`, it will use // those instead of the stock `#![allow(unused)]`. - let mut opts = TestOptions::default(); + let mut opts = GlobalTestOptions::default(); opts.attrs.push("feature(sick_rad)".to_string()); let input = "use asdf::qwop; assert_eq!(2+2, 4);"; @@ -155,7 +155,7 @@ assert_eq!(2+2, 4); fn make_test_crate_attrs() { // Including inner attributes in your doctest will apply them to the whole "crate", pasting // them outside the generated main function. - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "#![feature(sick_rad)] assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -171,7 +171,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_with_main() { // Including your own `fn main` wrapper lets the test use it verbatim. - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "fn main() { assert_eq!(2+2, 4); }"; @@ -187,7 +187,7 @@ fn main() { #[test] fn make_test_fake_main() { // ... but putting it in a comment will still provide a wrapper. - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "//Ceci n'est pas une `fn main` assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -203,7 +203,7 @@ assert_eq!(2+2, 4); #[test] fn make_test_dont_insert_main() { // Even with that, if you set `dont_insert_main`, it won't create the `fn main` wrapper. - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "//Ceci n'est pas une `fn main` assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] @@ -216,7 +216,7 @@ assert_eq!(2+2, 4);" #[test] fn make_test_issues_21299_33731() { - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "// fn main assert_eq!(2+2, 4);"; @@ -248,7 +248,7 @@ assert_eq!(asdf::foo, 4); #[test] fn make_test_main_in_macro() { - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "#[macro_use] extern crate my_crate; test_wrapper! { fn main() {} @@ -267,7 +267,7 @@ test_wrapper! { #[test] fn make_test_returns_result() { // creates an inner function and unwraps it - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "use std::io; let mut input = String::new(); io::stdin().read_line(&mut input)?; @@ -287,7 +287,7 @@ Ok::<(), io:Error>(()) #[test] fn make_test_named_wrapper() { // creates an inner function with a specific name - let opts = TestOptions::default(); + let opts = GlobalTestOptions::default(); let input = "assert_eq!(2+2, 4);"; let expected = "#![allow(unused)] fn main() { #[allow(non_snake_case)] fn _doctest_main__some_unique_name() { diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index abb4bec5ca133..906b8f8a24570 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -7,7 +7,7 @@ use rustc_span::source_map::DUMMY_SP; use rustc_span::Symbol; use crate::config::{Options, RenderOptions}; -use crate::doctest::{Collector, TestOptions}; +use crate::doctest::{Collector, GlobalTestOptions}; use crate::html::escape::Escape; use crate::html::markdown; use crate::html::markdown::{ @@ -129,7 +129,7 @@ crate fn render>( crate fn test(options: Options) -> Result<(), String> { let input_str = read_to_string(&options.input) .map_err(|err| format!("{}: {}", options.input.display(), err))?; - let mut opts = TestOptions::default(); + let mut opts = GlobalTestOptions::default(); opts.no_crate_inject = true; let mut collector = Collector::new( Symbol::intern(&options.input.display().to_string()),