From 021d1fbd007456e6a8f2a7b865545352afb66a3f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 7 Oct 2022 13:57:32 +1100 Subject: [PATCH 1/9] Clean up rustdoc startup. rustc's startup has several layers, including: - `interface::run_compiler` passes a closure, `f`, to `run_in_thread_pool_with_globals`, which creates a thread pool, sets up session globals, and passes `f` to `create_compiler_and_run`. - `create_compiler_and_run` creates a `Session`, a `Compiler`, sets the source map, and calls `f`. rustdoc is a bit different. - `main_args` calls `main_options` via `run_in_thread_pool_with_globals`, which (again) creates a thread pool (hardcoded to a single thread!) and sets up session globals. - `main_options` has four different paths. - The second one calls `interface::run_compiler`, which redoes the `run_in_thread_pool_with_globals`! This is bad. - The fourth one calls `interface::create_compiler_and_run`, which is reasonable. - The first and third ones don't do anything of note involving the above functions, except for some symbol interning which requires session globals. In other words, rustdoc calls into `rustc_interface` at three different levels. It's a bit confused, and feels like code where functionality has been added by different people at different times without fully understanding how the globally accessible stuff is set up. This commit tidies things up. It removes the `run_in_thread_pool_with_globals` call in `main_args`, and adjust the four paths in `main_options` as follows. - `markdown::test` calls `test::test_main`, which provides its own parallelism and so doesn't need a thread pool. It had one small use of symbol interning, which required session globals, but the commit removes this. - `doctest::run` already calls `interface::run_compiler`, so it doesn't need further adjustment. - `markdown::render` is simple but needs session globals for interning (which can't easily be removed), so it's now wrapped in `create_session_globals_then`. - The fourth path now uses `interface::run_compiler`, which is equivalent to the old `run_in_thread_pool_with_globals` + `create_compiler_and_run` pairing. --- src/librustdoc/doctest.rs | 9 ++++----- src/librustdoc/lib.rs | 14 +++++++------- src/librustdoc/markdown.rs | 5 +++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index ac8b5211878f2..58f02e3da557f 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -18,7 +18,6 @@ use rustc_session::{lint, Session}; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; -use rustc_span::Symbol; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::TargetTriple; use tempfile::Builder as TempFileBuilder; @@ -124,7 +123,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let opts = scrape_test_config(crate_attrs); let enable_per_target_ignores = options.enable_per_target_ignores; let mut collector = Collector::new( - tcx.crate_name(LOCAL_CRATE), + tcx.crate_name(LOCAL_CRATE).to_string(), options, false, opts, @@ -908,7 +907,7 @@ pub(crate) struct Collector { rustdoc_options: RustdocOptions, use_headers: bool, enable_per_target_ignores: bool, - crate_name: Symbol, + crate_name: String, opts: GlobalTestOptions, position: Span, source_map: Option>, @@ -920,7 +919,7 @@ pub(crate) struct Collector { impl Collector { pub(crate) fn new( - crate_name: Symbol, + crate_name: String, rustdoc_options: RustdocOptions, use_headers: bool, opts: GlobalTestOptions, @@ -983,7 +982,7 @@ impl Tester for Collector { fn add_test(&mut self, test: String, config: LangString, line: usize) { let filename = self.get_filename(); let name = self.generate_name(line, &filename); - let crate_name = self.crate_name.to_string(); + let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); let edition = config.edition.unwrap_or(self.rustdoc_options.edition); let rustdoc_options = self.rustdoc_options.clone(); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ef01b854e5a69..ce6f7e817c620 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -700,11 +700,7 @@ fn main_args(at_args: &[String]) -> MainResult { }; } }; - rustc_interface::util::run_in_thread_pool_with_globals( - options.edition, - 1, // this runs single-threaded, even in a parallel compiler - move || main_options(options), - ) + main_options(options) } fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult { @@ -749,9 +745,12 @@ fn main_options(options: config::Options) -> MainResult { (true, true) => return wrap_return(&diag, markdown::test(options)), (true, false) => return doctest::run(options), (false, true) => { + // Session globals are required for symbol interning. return wrap_return( &diag, - markdown::render(&options.input, options.render_options, options.edition), + rustc_span::create_session_globals_then(options.edition, || { + markdown::render(&options.input, options.render_options, options.edition) + }), ); } (false, false) => {} @@ -777,9 +776,10 @@ fn main_options(options: config::Options) -> MainResult { let render_options = options.render_options.clone(); let scrape_examples_options = options.scrape_examples_options.clone(); let document_private = options.render_options.document_private; + let config = core::create_config(options); - interface::create_compiler_and_run(config, |compiler| { + interface::run_compiler(config, |compiler| { let sess = compiler.session(); if sess.opts.describe_lints { diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 0b557ef244e94..eb64ac455dc4c 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -5,7 +5,6 @@ use std::path::Path; use rustc_span::edition::Edition; use rustc_span::source_map::DUMMY_SP; -use rustc_span::Symbol; use crate::config::{Options, RenderOptions}; use crate::doctest::{Collector, GlobalTestOptions}; @@ -36,6 +35,8 @@ fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) { /// Render `input` (e.g., "foo.md") into an HTML file in `output` /// (e.g., output = "bar" => "bar/foo.html"). +/// +/// Requires session globals to be available, for symbol interning. pub(crate) fn render>( input: P, options: RenderOptions, @@ -133,7 +134,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { let mut opts = GlobalTestOptions::default(); opts.no_crate_inject = true; let mut collector = Collector::new( - Symbol::intern(&options.input.display().to_string()), + options.input.display().to_string(), options.clone(), true, opts, From 2a62c92b25a8fbaf0fdd0fa8f8baabf7be9d9a91 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 7 Oct 2022 15:36:44 +1100 Subject: [PATCH 2/9] Merge `main_options` into `main_args`. There is no longer any need for them to be separate. --- src/librustdoc/lib.rs | 57 ++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ce6f7e817c620..7e0cc668d7b59 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -674,35 +674,6 @@ fn usage(argv0: &str) { /// A result type used by several functions under `main()`. type MainResult = Result<(), ErrorGuaranteed>; -fn main_args(at_args: &[String]) -> MainResult { - let args = rustc_driver::args::arg_expand_all(at_args); - - let mut options = getopts::Options::new(); - for option in opts() { - (option.apply)(&mut options); - } - let matches = match options.parse(&args[1..]) { - Ok(m) => m, - Err(err) => { - early_error(ErrorOutputType::default(), &err.to_string()); - } - }; - - // Note that we discard any distinction between different non-zero exit - // codes from `from_matches` here. - let options = match config::Options::from_matches(&matches, args) { - Ok(opts) => opts, - Err(code) => { - return if code == 0 { - Ok(()) - } else { - Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()) - }; - } - }; - main_options(options) -} - fn wrap_return(diag: &rustc_errors::Handler, res: Result<(), String>) -> MainResult { match res { Ok(()) => Ok(()), @@ -733,7 +704,33 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>( } } -fn main_options(options: config::Options) -> MainResult { +fn main_args(at_args: &[String]) -> MainResult { + let args = rustc_driver::args::arg_expand_all(at_args); + + let mut options = getopts::Options::new(); + for option in opts() { + (option.apply)(&mut options); + } + let matches = match options.parse(&args[1..]) { + Ok(m) => m, + Err(err) => { + early_error(ErrorOutputType::default(), &err.to_string()); + } + }; + + // Note that we discard any distinction between different non-zero exit + // codes from `from_matches` here. + let options = match config::Options::from_matches(&matches, args) { + Ok(opts) => opts, + Err(code) => { + return if code == 0 { + Ok(()) + } else { + Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()) + }; + } + }; + let diag = core::new_handler( options.error_format, None, From 134e9d36ce5316c0da61923c0182dc547db9ba46 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 7 Oct 2022 11:57:25 +1100 Subject: [PATCH 3/9] Inline and remove `scoped_thread`. It has a single call site, and removing it slightly improves the confusing tangle of nested closures present at startup. --- compiler/rustc_interface/src/util.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 3a9e491e28965..fa2a085f53c9d 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -130,33 +130,27 @@ fn get_stack_size() -> Option { env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE) } -/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need -/// for `'static` bounds. -#[cfg(not(parallel_compiler))] -fn scoped_thread R + Send, R: Send>(cfg: thread::Builder, f: F) -> R { - // SAFETY: join() is called immediately, so any closure captures are still - // alive. - match unsafe { cfg.spawn_unchecked(f) }.unwrap().join() { - Ok(v) => v, - Err(e) => panic::resume_unwind(e), - } -} - #[cfg(not(parallel_compiler))] pub fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, _threads: usize, f: F, ) -> R { + // The thread pool is a single thread in the non-parallel compiler. let mut cfg = thread::Builder::new().name("rustc".to_string()); - if let Some(size) = get_stack_size() { cfg = cfg.stack_size(size); } - let main_handler = move || rustc_span::create_session_globals_then(edition, f); + let f = move || rustc_span::create_session_globals_then(edition, f); - scoped_thread(cfg, main_handler) + // This avoids the need for `'static` bounds. + // + // SAFETY: join() is called immediately, so any closure captures are still alive. + match unsafe { cfg.spawn_unchecked(f) }.unwrap().join() { + Ok(v) => v, + Err(e) => panic::resume_unwind(e), + } } /// Creates a new thread and forwards information in thread locals to it. From dcc194e4bfb879220306538efe2666b0381b11c1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 7 Oct 2022 15:35:42 +1100 Subject: [PATCH 4/9] Reduce visibility of some functions. --- compiler/rustc_interface/src/interface.rs | 2 +- compiler/rustc_interface/src/util.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 134934c7ca6b2..71d59c76dbc34 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -275,7 +275,7 @@ pub struct Config { pub registry: Registry, } -pub fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R) -> R { +fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R) -> R { crate::callbacks::setup_callbacks(); let registry = &config.registry; diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index fa2a085f53c9d..c07d562332b78 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -131,7 +131,7 @@ fn get_stack_size() -> Option { } #[cfg(not(parallel_compiler))] -pub fn run_in_thread_pool_with_globals R + Send, R: Send>( +pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, _threads: usize, f: F, @@ -169,7 +169,7 @@ unsafe fn handle_deadlock() { } #[cfg(parallel_compiler)] -pub fn run_in_thread_pool_with_globals R + Send, R: Send>( +pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, threads: usize, f: F, From b6ae1453cbe677deea362628c69efa501b4faa4b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 7 Oct 2022 16:17:57 +1100 Subject: [PATCH 5/9] Inline and remove `create_compiler_and_run`. It has a single call site. --- compiler/rustc_interface/src/interface.rs | 104 +++++++++++----------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 71d59c76dbc34..59b89ce01a62b 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -275,58 +275,6 @@ pub struct Config { pub registry: Registry, } -fn create_compiler_and_run(config: Config, f: impl FnOnce(&Compiler) -> R) -> R { - crate::callbacks::setup_callbacks(); - - let registry = &config.registry; - let (mut sess, codegen_backend) = util::create_session( - config.opts, - config.crate_cfg, - config.crate_check_cfg, - config.file_loader, - config.input_path.clone(), - config.lint_caps, - config.make_codegen_backend, - registry.clone(), - ); - - if let Some(parse_sess_created) = config.parse_sess_created { - parse_sess_created( - &mut Lrc::get_mut(&mut sess) - .expect("create_session() should never share the returned session") - .parse_sess, - ); - } - - let temps_dir = sess.opts.unstable_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o)); - - let compiler = Compiler { - sess, - codegen_backend, - input: config.input, - input_path: config.input_path, - output_dir: config.output_dir, - output_file: config.output_file, - temps_dir, - register_lints: config.register_lints, - override_queries: config.override_queries, - }; - - rustc_span::with_source_map(compiler.sess.parse_sess.clone_source_map(), move || { - let r = { - let _sess_abort_error = OnDrop(|| { - compiler.sess.finish_diagnostics(registry); - }); - - f(&compiler) - }; - - let prof = compiler.sess.prof.clone(); - prof.generic_activity("drop_compiler").run(move || drop(compiler)); - r - }) -} - // JUSTIFICATION: before session exists, only config #[allow(rustc::bad_opt_access)] pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { @@ -334,7 +282,57 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se util::run_in_thread_pool_with_globals( config.opts.edition, config.opts.unstable_opts.threads, - || create_compiler_and_run(config, f), + || { + crate::callbacks::setup_callbacks(); + + let registry = &config.registry; + let (mut sess, codegen_backend) = util::create_session( + config.opts, + config.crate_cfg, + config.crate_check_cfg, + config.file_loader, + config.input_path.clone(), + config.lint_caps, + config.make_codegen_backend, + registry.clone(), + ); + + if let Some(parse_sess_created) = config.parse_sess_created { + parse_sess_created( + &mut Lrc::get_mut(&mut sess) + .expect("create_session() should never share the returned session") + .parse_sess, + ); + } + + let temps_dir = sess.opts.unstable_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o)); + + let compiler = Compiler { + sess, + codegen_backend, + input: config.input, + input_path: config.input_path, + output_dir: config.output_dir, + output_file: config.output_file, + temps_dir, + register_lints: config.register_lints, + override_queries: config.override_queries, + }; + + rustc_span::with_source_map(compiler.sess.parse_sess.clone_source_map(), move || { + let r = { + let _sess_abort_error = OnDrop(|| { + compiler.sess.finish_diagnostics(registry); + }); + + f(&compiler) + }; + + let prof = compiler.sess.prof.clone(); + prof.generic_activity("drop_compiler").run(move || drop(compiler)); + r + }) + }, ) } From ec409f95bf52c5891084e309fed91fa9b6ce10b4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 7 Oct 2022 16:20:20 +1100 Subject: [PATCH 6/9] Apply `Lrc` later to `sess` and `codegen_backend`. This avoids the need for a degenerate `Lrc::get_mut` call. --- compiler/rustc_interface/src/interface.rs | 10 +++------- compiler/rustc_interface/src/util.rs | 5 ++--- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 59b89ce01a62b..a3bf7cde9ff71 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -298,18 +298,14 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se ); if let Some(parse_sess_created) = config.parse_sess_created { - parse_sess_created( - &mut Lrc::get_mut(&mut sess) - .expect("create_session() should never share the returned session") - .parse_sess, - ); + parse_sess_created(&mut sess.parse_sess); } let temps_dir = sess.opts.unstable_opts.temps_dir.as_ref().map(|o| PathBuf::from(&o)); let compiler = Compiler { - sess, - codegen_backend, + sess: Lrc::new(sess), + codegen_backend: Lrc::new(codegen_backend), input: config.input, input_path: config.input_path, output_dir: config.output_dir, diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index c07d562332b78..afc43745b1827 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -5,7 +5,6 @@ use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[cfg(parallel_compiler)] use rustc_data_structures::jobserver; -use rustc_data_structures::sync::Lrc; use rustc_errors::registry::Registry; #[cfg(parallel_compiler)] use rustc_middle::ty::tls; @@ -72,7 +71,7 @@ pub fn create_session( Box Box + Send>, >, descriptions: Registry, -) -> (Lrc, Lrc>) { +) -> (Session, Box) { let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend { make_codegen_backend(&sopts) } else { @@ -119,7 +118,7 @@ pub fn create_session( sess.parse_sess.config = cfg; sess.parse_sess.check_config = check_cfg; - (Lrc::new(sess), Lrc::new(codegen_backend)) + (sess, codegen_backend) } const STACK_SIZE: usize = 8 * 1024 * 1024; From 63db9e540c4d358c5a0e63423fc16fa9a7979155 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 8 Oct 2022 07:43:15 +1100 Subject: [PATCH 7/9] Replace a `spawn_unchecked` with `spawn_scoped`. --- compiler/rustc_interface/src/util.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index afc43745b1827..6d0fffc152c3c 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -136,20 +136,24 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( f: F, ) -> R { // The thread pool is a single thread in the non-parallel compiler. - let mut cfg = thread::Builder::new().name("rustc".to_string()); - if let Some(size) = get_stack_size() { - cfg = cfg.stack_size(size); - } + thread::scope(|s| { + let mut builder = thread::Builder::new().name("rustc".to_string()); + if let Some(size) = get_stack_size() { + builder = builder.stack_size(size); + } - let f = move || rustc_span::create_session_globals_then(edition, f); + // `unwrap` is ok here because `spawn_scoped` only panics if the thread + // name contains null bytes. + let r = builder + .spawn_scoped(s, move || rustc_span::create_session_globals_then(edition, f)) + .unwrap() + .join(); - // This avoids the need for `'static` bounds. - // - // SAFETY: join() is called immediately, so any closure captures are still alive. - match unsafe { cfg.spawn_unchecked(f) }.unwrap().join() { - Ok(v) => v, - Err(e) => panic::resume_unwind(e), - } + match r { + Ok(v) => v, + Err(e) => panic::resume_unwind(e), + } + }) } /// Creates a new thread and forwards information in thread locals to it. From 38988e62bc7672e1e85b7db4e3a885608d9a3888 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 17 Oct 2022 10:19:46 +1100 Subject: [PATCH 8/9] Use `interface::run_compiler` for `markdown::render`. It turns out `markdown::render` is more complex than it first appears, because it can invoke `doctest::make_test`, which requires session globals and a thread pool. So this commit changes it to use `interface::run_compiler`. Three of the four paths in `main_args` now use `interface::run_compiler`. --- src/librustdoc/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 7e0cc668d7b59..e78b0c9199cf4 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -742,11 +742,18 @@ fn main_args(at_args: &[String]) -> MainResult { (true, true) => return wrap_return(&diag, markdown::test(options)), (true, false) => return doctest::run(options), (false, true) => { - // Session globals are required for symbol interning. + let input = options.input.clone(); + let render_options = options.render_options.clone(); + let edition = options.edition; + let config = core::create_config(options); + + // `markdown::render` can invoke `doctest::make_test`, which + // requires session globals and a thread pool, so we use + // `run_compiler`. return wrap_return( &diag, - rustc_span::create_session_globals_then(options.edition, || { - markdown::render(&options.input, options.render_options, options.edition) + interface::run_compiler(config, |_compiler| { + markdown::render(&input, render_options, edition) }), ); } From ca2561a07b01841ceef6790d78e12aaa1d2e3aec Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 17 Oct 2022 10:51:40 +1100 Subject: [PATCH 9/9] Avoid cloning `RenderOptions`. By moving `RenderOptions` out of `Option`, because the two structs' uses are almost entirely separate. The only complication is that `unstable_features` is needed in both structs, but it's a tiny `Copy` type so its duplication seems fine. --- src/librustdoc/config.rs | 80 ++++++++++++++------------- src/librustdoc/doctest.rs | 2 +- src/librustdoc/html/render/context.rs | 3 +- src/librustdoc/lib.rs | 8 +-- src/librustdoc/markdown.rs | 2 +- 5 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 932533db05c14..67ea39fb96579 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -142,8 +142,6 @@ pub(crate) struct Options { // Options that alter generated documentation pages /// Crate version to note on the sidebar of generated docs. pub(crate) crate_version: Option, - /// Collected options specific to outputting final pages. - pub(crate) render_options: RenderOptions, /// The format that we output when rendering. /// /// Currently used only for the `--show-coverage` option. @@ -159,6 +157,10 @@ pub(crate) struct Options { /// Configuration for scraping examples from the current crate. If this option is Some(..) then /// the compiler will scrape examples and not generate documentation. pub(crate) scrape_examples_options: Option, + + /// Note: this field is duplicated in `RenderOptions` because it's useful + /// to have it in both places. + pub(crate) unstable_features: rustc_feature::UnstableFeatures, } impl fmt::Debug for Options { @@ -194,7 +196,6 @@ impl fmt::Debug for Options { .field("persist_doctests", &self.persist_doctests) .field("show_coverage", &self.show_coverage) .field("crate_version", &self.crate_version) - .field("render_options", &self.render_options) .field("runtool", &self.runtool) .field("runtool_args", &self.runtool_args) .field("enable-per-target-ignores", &self.enable_per_target_ignores) @@ -202,6 +203,7 @@ impl fmt::Debug for Options { .field("no_run", &self.no_run) .field("nocapture", &self.nocapture) .field("scrape_examples_options", &self.scrape_examples_options) + .field("unstable_features", &self.unstable_features) .finish() } } @@ -267,6 +269,8 @@ pub(crate) struct RenderOptions { pub(crate) generate_redirect_map: bool, /// Show the memory layout of types in the docs. pub(crate) show_type_layout: bool, + /// Note: this field is duplicated in `Options` because it's useful to have + /// it in both places. pub(crate) unstable_features: rustc_feature::UnstableFeatures, pub(crate) emit: Vec, /// If `true`, HTML source pages will generate links for items to their definition. @@ -316,7 +320,7 @@ impl Options { pub(crate) fn from_matches( matches: &getopts::Matches, args: Vec, - ) -> Result { + ) -> Result<(Options, RenderOptions), i32> { let args = &args[1..]; // Check for unstable options. nightly_options::check_nightly_options(matches, &opts()); @@ -710,7 +714,9 @@ impl Options { let with_examples = matches.opt_strs("with-examples"); let call_locations = crate::scrape_examples::load_call_locations(with_examples, &diag)?; - Ok(Options { + let unstable_features = + rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref()); + let options = Options { input, proc_macro_crate, error_format, @@ -744,42 +750,42 @@ impl Options { run_check, no_run, nocapture, - render_options: RenderOptions { - output, - external_html, - id_map, - playground_url, - module_sorting, - themes, - extension_css, - extern_html_root_urls, - extern_html_root_takes_precedence, - default_settings, - resource_suffix, - enable_minification, - enable_index_page, - index_page, - static_root_path, - markdown_no_toc, - markdown_css, - markdown_playground_url, - document_private, - document_hidden, - generate_redirect_map, - show_type_layout, - unstable_features: rustc_feature::UnstableFeatures::from_environment( - crate_name.as_deref(), - ), - emit, - generate_link_to_definition, - call_locations, - no_emit_shared: false, - }, crate_name, output_format, json_unused_externs, scrape_examples_options, - }) + unstable_features, + }; + let render_options = RenderOptions { + output, + external_html, + id_map, + playground_url, + module_sorting, + themes, + extension_css, + extern_html_root_urls, + extern_html_root_takes_precedence, + default_settings, + resource_suffix, + enable_minification, + enable_index_page, + index_page, + static_root_path, + markdown_no_toc, + markdown_css, + markdown_playground_url, + document_private, + document_hidden, + generate_redirect_map, + show_type_layout, + unstable_features, + emit, + generate_link_to_definition, + call_locations, + no_emit_shared: false, + }; + Ok((options, render_options)) } /// Returns `true` if the file given as `self.input` is a Markdown file. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 58f02e3da557f..cb216970c7c35 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -79,7 +79,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { lint_cap: Some(options.lint_cap.unwrap_or(lint::Forbid)), cg: options.codegen_options.clone(), externs: options.externs.clone(), - unstable_features: options.render_options.unstable_features, + unstable_features: options.unstable_features, actually_rustdoc: true, edition: options.edition, target_triple: options.target.clone(), diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index e303dd8bdaf13..5733d1f9c79d6 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -430,7 +430,6 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { extension_css, resource_suffix, static_root_path, - unstable_features, generate_redirect_map, show_type_layout, generate_link_to_definition, @@ -511,7 +510,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { resource_suffix, static_root_path, fs: DocFS::new(sender), - codes: ErrorCodes::from(unstable_features.is_nightly_build()), + codes: ErrorCodes::from(options.unstable_features.is_nightly_build()), playground, all: RefCell::new(AllTypes::new()), errors: receiver, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index e78b0c9199cf4..793061a9f7a06 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -720,7 +720,7 @@ fn main_args(at_args: &[String]) -> MainResult { // Note that we discard any distinction between different non-zero exit // codes from `from_matches` here. - let options = match config::Options::from_matches(&matches, args) { + let (options, render_options) = match config::Options::from_matches(&matches, args) { Ok(opts) => opts, Err(code) => { return if code == 0 { @@ -743,7 +743,6 @@ fn main_args(at_args: &[String]) -> MainResult { (true, false) => return doctest::run(options), (false, true) => { let input = options.input.clone(); - let render_options = options.render_options.clone(); let edition = options.edition; let config = core::create_config(options); @@ -775,11 +774,8 @@ fn main_args(at_args: &[String]) -> MainResult { let crate_version = options.crate_version.clone(); let output_format = options.output_format; - // FIXME: fix this clone (especially render_options) let externs = options.externs.clone(); - let render_options = options.render_options.clone(); let scrape_examples_options = options.scrape_examples_options.clone(); - let document_private = options.render_options.document_private; let config = core::create_config(options); @@ -815,7 +811,7 @@ fn main_args(at_args: &[String]) -> MainResult { sess, krate, externs, - document_private, + render_options.document_private, ) }); (resolver.clone(), resolver_caches) diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index eb64ac455dc4c..044e051440c52 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -143,7 +143,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { options.enable_per_target_ignores, ); collector.set_position(DUMMY_SP); - let codes = ErrorCodes::from(options.render_options.unstable_features.is_nightly_build()); + let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None);