| // tidy-alphabetical-start | |
| #![doc( | |
| html_root_url = "https://doc.rust-lang.org/nightly/", | |
| html_playground_url = "https://play.rust-lang.org/" | |
| )] | |
| #![feature(ascii_char)] | |
| #![feature(ascii_char_variants)] | |
| #![feature(deref_patterns)] | |
| #![feature(file_buffered)] | |
| #![feature(formatting_options)] | |
| #![feature(iter_intersperse)] | |
| #![feature(iter_order_by)] | |
| #![feature(rustc_private)] | |
| #![feature(test)] | |
| #![feature(trim_prefix_suffix)] | |
| #![feature(variant_count)] | |
| #![recursion_limit = "256"] | |
| #![warn(rustc::internal)] | |
| #![warn(rustc::symbol_intern_string_literal)] | |
| // tidy-alphabetical-end | |
| // N.B. these need `extern crate` even in 2018 edition | |
| // because they're loaded implicitly from the sysroot. | |
| // The reason they're loaded from the sysroot is because | |
| // the rustdoc artifacts aren't stored in rustc's cargo target directory. | |
| // So if `rustc` was specified in Cargo.toml, this would spuriously rebuild crates. | |
| // | |
| // Dependencies listed in Cargo.toml do not need `extern crate`. | |
| extern crate rustc_abi; | |
| extern crate rustc_ast; | |
| extern crate rustc_ast_pretty; | |
| extern crate rustc_attr_parsing; | |
| extern crate rustc_data_structures; | |
| extern crate rustc_driver; | |
| extern crate rustc_errors; | |
| extern crate rustc_feature; | |
| extern crate rustc_hir; | |
| extern crate rustc_hir_analysis; | |
| extern crate rustc_hir_pretty; | |
| extern crate rustc_index; | |
| extern crate rustc_infer; | |
| extern crate rustc_interface; | |
| extern crate rustc_lexer; | |
| extern crate rustc_lint; | |
| extern crate rustc_lint_defs; | |
| extern crate rustc_log; | |
| extern crate rustc_macros; | |
| extern crate rustc_metadata; | |
| extern crate rustc_middle; | |
| extern crate rustc_parse; | |
| extern crate rustc_passes; | |
| extern crate rustc_resolve; | |
| extern crate rustc_serialize; | |
| extern crate rustc_session; | |
| extern crate rustc_span; | |
| extern crate rustc_target; | |
| extern crate rustc_trait_selection; | |
| extern crate test; | |
| use std::env::{self, VarError}; | |
| use std::io::{self, IsTerminal}; | |
| use std::path::Path; | |
| use std::process::ExitCode; | |
| use rustc_ast::ast; | |
| use rustc_errors::DiagCtxtHandle; | |
| use rustc_hir::def_id::LOCAL_CRATE; | |
| use rustc_interface::interface; | |
| use rustc_middle::ty::TyCtxt; | |
| use rustc_session::config::{ErrorOutputType, RustcOptGroup, make_crate_type_option}; | |
| use rustc_session::{EarlyDiagCtxt, getopts}; | |
| use rustc_span::{BytePos, Span, SyntaxContext}; | |
| use tracing::info; | |
| use crate::clean::utils::DOC_RUST_LANG_ORG_VERSION; | |
| use crate::config::EmitType; | |
| use crate::error::Error; | |
| use crate::formats::cache::Cache; | |
| /// A macro to create a FxHashMap. | |
| /// | |
| /// Example: | |
| /// | |
| /// ```ignore(cannot-test-this-because-non-exported-macro) | |
| /// let letters = map!{"a" => "b", "c" => "d"}; | |
| /// ``` | |
| /// | |
| /// Trailing commas are allowed. | |
| /// Commas between elements are required (even if the expression is a block). | |
| macro_rules! map { | |
| ($( $key: expr => $val: expr ),* $(,)*) => {{ | |
| let mut map = ::rustc_data_structures::fx::FxIndexMap::default(); | |
| $( map.insert($key, $val); )* | |
| map | |
| }} | |
| } | |
| mod calculate_doc_coverage; | |
| mod clean; | |
| mod config; | |
| mod core; | |
| mod display; | |
| mod docfs; | |
| mod doctest; | |
| mod error; | |
| mod externalfiles; | |
| mod fold; | |
| mod formats; | |
| // used by the error-index generator, so it needs to be public | |
| pub mod html; | |
| mod json; | |
| pub(crate) mod lint; | |
| mod markdown; | |
| mod passes; | |
| mod scrape_examples; | |
| mod theme; | |
| mod visit; | |
| mod visit_ast; | |
| mod visit_lib; | |
| pub fn main() -> ExitCode { | |
| let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); | |
| rustc_driver::install_ice_hook( | |
| "https://github.com/rust-lang/rust/issues/new\ | |
| ?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md", | |
| |_| (), | |
| ); | |
| // When using CI artifacts with `download-rustc`, tracing is unconditionally built | |
| // with `--features=static_max_level_info`, which disables almost all rustdoc logging. To avoid | |
| // this, compile our own version of `tracing` that logs all levels. | |
| // NOTE: this compiles both versions of tracing unconditionally, because | |
| // - The compile time hit is not that bad, especially compared to rustdoc's incremental times, and | |
| // - Otherwise, there's no warning that logging is being ignored when `download-rustc` is enabled | |
| crate::init_logging(&early_dcx); | |
| match rustc_log::init_logger(rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")) { | |
| Ok(()) => {} | |
| // With `download-rustc = true` there are definitely 2 distinct tracing crates in the | |
| // dependency graph: one in the downloaded sysroot and one built just now as a dependency of | |
| // rustdoc. So the sysroot's tracing is definitely not yet initialized here. | |
| // | |
| // But otherwise, depending on link style, there may or may not be 2 tracing crates in play. | |
| // The one we just initialized in `crate::init_logging` above is rustdoc's direct dependency | |
| // on tracing. When rustdoc is built by x.py using Cargo, rustc_driver's and rustc_log's | |
| // tracing dependency is distinct from this one and also needs to be initialized (using the | |
| // same RUSTDOC_LOG environment variable for both). Other build systems may use just a | |
| // single tracing crate throughout the rustc and rustdoc build. | |
| // | |
| // The reason initializing 2 tracings does not show double logging when `download-rustc = | |
| // false` and `debug_logging = true` is because all rustc logging goes only to its version | |
| // of tracing (the one in the sysroot) and all of rustdoc's logging only goes to its version | |
| // (the one in Cargo.toml). | |
| Err(rustc_log::Error::AlreadyInit(_)) => {} | |
| Err(error) => early_dcx.early_fatal(error.to_string()), | |
| } | |
| rustc_driver::catch_with_exit_code(|| { | |
| let at_args = rustc_driver::args::raw_args(&early_dcx); | |
| main_args(&mut early_dcx, &at_args); | |
| }) | |
| } | |
| fn init_logging(early_dcx: &EarlyDiagCtxt) { | |
| let color_logs = match env::var("RUSTDOC_LOG_COLOR").as_deref() { | |
| Ok("always") => true, | |
| Ok("never") => false, | |
| Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(), | |
| Ok(value) => early_dcx.early_fatal(format!( | |
| "invalid log color value '{value}': expected one of always, never, or auto", | |
| )), | |
| Err(VarError::NotUnicode(value)) => early_dcx.early_fatal(format!( | |
| "invalid log color value '{}': expected one of always, never, or auto", | |
| value.to_string_lossy() | |
| )), | |
| }; | |
| let filter = tracing_subscriber::EnvFilter::from_env("RUSTDOC_LOG"); | |
| let layer = tracing_tree::HierarchicalLayer::default() | |
| .with_writer(io::stderr) | |
| .with_ansi(color_logs) | |
| .with_targets(true) | |
| .with_wraparound(10) | |
| .with_verbose_exit(true) | |
| .with_verbose_entry(true) | |
| .with_indent_amount(2); | |
| #[cfg(debug_assertions)] | |
| let layer = layer.with_thread_ids(true).with_thread_names(true); | |
| use tracing_subscriber::layer::SubscriberExt; | |
| let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); | |
| tracing::subscriber::set_global_default(subscriber).unwrap(); | |
| } | |
| fn opts() -> Vec<RustcOptGroup> { | |
| use rustc_session::config::OptionKind::{Flag, FlagMulti, Multi, Opt}; | |
| use rustc_session::config::OptionStability::{Stable, Unstable}; | |
| use rustc_session::config::make_opt as opt; | |
| vec![ | |
| opt(Stable, FlagMulti, "h", "help", "show this help message", ""), | |
| opt(Stable, FlagMulti, "V", "version", "print rustdoc's version", ""), | |
| opt(Stable, FlagMulti, "v", "verbose", "use verbose output", ""), | |
| opt(Stable, Opt, "w", "output-format", "the output type to write", "[html]"), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "output", | |
| "Which directory to place the output. This option is deprecated, use --out-dir instead.", | |
| "PATH", | |
| ), | |
| opt(Stable, Opt, "o", "out-dir", "which directory to place the output", "PATH"), | |
| opt(Stable, Opt, "", "crate-name", "specify the name of this crate", "NAME"), | |
| make_crate_type_option(), | |
| opt(Stable, Multi, "L", "library-path", "directory to add to crate search path", "DIR"), | |
| opt(Stable, Multi, "", "cfg", "pass a --cfg to rustc", ""), | |
| opt(Stable, Multi, "", "check-cfg", "pass a --check-cfg to rustc", ""), | |
| opt(Stable, Multi, "", "extern", "pass an --extern to rustc", "NAME[=PATH]"), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "extern-html-root-url", | |
| "base URL to use for dependencies; for example, \ | |
| \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html", | |
| "NAME=URL", | |
| ), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "extern-html-root-takes-precedence", | |
| "give precedence to `--extern-html-root-url`, not `html_root_url`", | |
| "", | |
| ), | |
| opt(Stable, Multi, "C", "codegen", "pass a codegen option to rustc", "OPT[=VALUE]"), | |
| opt(Stable, FlagMulti, "", "document-private-items", "document private items", ""), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "document-hidden-items", | |
| "document items that have doc(hidden)", | |
| "", | |
| ), | |
| opt(Stable, FlagMulti, "", "test", "run code examples as tests", ""), | |
| opt(Stable, Multi, "", "test-args", "arguments to pass to the test runner", "ARGS"), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "test-run-directory", | |
| "The working directory in which to run tests", | |
| "PATH", | |
| ), | |
| opt(Stable, Opt, "", "target", "target triple to document", "TRIPLE"), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "markdown-css", | |
| "CSS files to include via <link> in a rendered Markdown file", | |
| "FILES", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "html-in-header", | |
| "files to include inline in the <head> section of a rendered Markdown file \ | |
| or generated documentation", | |
| "FILES", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "html-before-content", | |
| "files to include inline between <body> and the content of a rendered \ | |
| Markdown file or generated documentation", | |
| "FILES", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "html-after-content", | |
| "files to include inline between the content and </body> of a rendered \ | |
| Markdown file or generated documentation", | |
| "FILES", | |
| ), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "markdown-before-content", | |
| "files to include inline between <body> and the content of a rendered \ | |
| Markdown file or generated documentation", | |
| "FILES", | |
| ), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "markdown-after-content", | |
| "files to include inline between the content and </body> of a rendered \ | |
| Markdown file or generated documentation", | |
| "FILES", | |
| ), | |
| opt(Stable, Opt, "", "markdown-playground-url", "URL to send code snippets to", "URL"), | |
| opt(Stable, FlagMulti, "", "markdown-no-toc", "don't include table of contents", ""), | |
| opt( | |
| Stable, | |
| Opt, | |
| "e", | |
| "extend-css", | |
| "To add some CSS rules with a given file to generate doc with your own theme. \ | |
| However, your theme might break if the rustdoc's generated HTML changes, so be careful!", | |
| "PATH", | |
| ), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "Z", | |
| "", | |
| "unstable / perma-unstable options (only on nightly build)", | |
| "FLAG", | |
| ), | |
| opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "playground-url", | |
| "URL to send code snippets to, may be reset by --markdown-playground-url \ | |
| or `#![doc(html_playground_url=...)]`", | |
| "URL", | |
| ), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "display-doctest-warnings", | |
| "show warnings that originate in doctests", | |
| "", | |
| ), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "crate-version", | |
| "crate version to print into documentation", | |
| "VERSION", | |
| ), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "sort-modules-by-appearance", | |
| "sort modules by where they appear in the program, rather than alphabetically", | |
| "", | |
| ), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "default-theme", | |
| "Set the default theme. THEME should be the theme name, generally lowercase. \ | |
| If an unknown default theme is specified, the builtin default is used. \ | |
| The set of themes, and the rustdoc built-in default, are not stable.", | |
| "THEME", | |
| ), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "default-setting", | |
| "Default value for a rustdoc setting (used when \"rustdoc-SETTING\" is absent \ | |
| from web browser Local Storage). If VALUE is not supplied, \"true\" is used. \ | |
| Supported SETTINGs and VALUEs are not documented and not stable.", | |
| "SETTING[=VALUE]", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "theme", | |
| "additional themes which will be added to the generated docs", | |
| "FILES", | |
| ), | |
| opt(Stable, Multi, "", "check-theme", "check if given theme is valid", "FILES"), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "resource-suffix", | |
| "suffix to add to CSS and JavaScript files, \ | |
| e.g., \"search-index.js\" will become \"search-index-suffix.js\"", | |
| "PATH", | |
| ), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "edition", | |
| "edition to use when compiling rust code (default: 2015)", | |
| "EDITION", | |
| ), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "color", | |
| "Configure coloring of output: | |
| auto = colorize, if output goes to a tty (default); | |
| always = always colorize output; | |
| never = never colorize output", | |
| "auto|always|never", | |
| ), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "error-format", | |
| "How errors and other messages are produced", | |
| "human|json|short", | |
| ), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "diagnostic-width", | |
| "Provide width of the output for truncated error messages", | |
| "WIDTH", | |
| ), | |
| opt(Stable, Opt, "", "json", "Configure the structure of JSON diagnostics", "CONFIG"), | |
| opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"), | |
| opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"), | |
| opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"), | |
| opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"), | |
| opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "cap-lints", | |
| "Set the most restrictive lint level. \ | |
| More restrictive lints are capped at this level. \ | |
| By default, it is at `forbid` level.", | |
| "LEVEL", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "remap-path-prefix", | |
| "Remap source names in compiler messages", | |
| "FROM=TO", | |
| ), | |
| opt(Unstable, Opt, "", "index-page", "Markdown file to be used as index page", "PATH"), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "enable-index-page", | |
| "To enable generation of the index page", | |
| "", | |
| ), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "static-root-path", | |
| "Path string to force loading static files from in output pages. \ | |
| If not set, uses combinations of '../' to reach the documentation root.", | |
| "PATH", | |
| ), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "persist-doctests", | |
| "Directory to persist doctest executables into", | |
| "PATH", | |
| ), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "show-coverage", | |
| "calculate percentage of public items with documentation", | |
| "", | |
| ), | |
| opt( | |
| Stable, | |
| Opt, | |
| "", | |
| "test-runtool", | |
| "", | |
| "The tool to run tests with when building for a different target than host", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "test-runtool-arg", | |
| "", | |
| "One argument (of possibly many) to pass to the runtool", | |
| ), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "test-builder", | |
| "The rustc-like binary to use as the test builder", | |
| "PATH", | |
| ), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "test-builder-wrapper", | |
| "Wrapper program to pass test-builder and arguments", | |
| "PATH", | |
| ), | |
| opt(Unstable, FlagMulti, "", "check", "Run rustdoc checks", ""), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "generate-redirect-map", | |
| "Generate JSON file at the top level instead of generating HTML redirection files", | |
| "", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "emit", | |
| "Comma separated list of types of output for rustdoc to emit", | |
| "[html-static-files,html-non-static-files,dep-info]", | |
| ), | |
| opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "merge-doctests", | |
| "Force all doctests to be compiled as a single binary, instead of one binary per test. If merging fails, rustdoc will emit a hard error.", | |
| "yes|no|auto", | |
| ), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "remap-path-scope", | |
| "Defines which scopes of paths should be remapped by `--remap-path-prefix`", | |
| "[macro,diagnostics,debuginfo,coverage,object,all]", | |
| ), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "show-type-layout", | |
| "Include the memory layout of types in the docs", | |
| "", | |
| ), | |
| opt(Unstable, Flag, "", "no-capture", "Don't capture stdout and stderr of tests", ""), | |
| opt( | |
| Unstable, | |
| Flag, | |
| "", | |
| "generate-link-to-definition", | |
| "Make the identifiers in the HTML source code pages navigable", | |
| "", | |
| ), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "scrape-examples-output-path", | |
| "", | |
| "collect function call information and output at the given path", | |
| ), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "scrape-examples-target-crate", | |
| "", | |
| "collect function call information for functions from the target crate", | |
| ), | |
| opt(Unstable, Flag, "", "scrape-tests", "Include test code when scraping examples", ""), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "with-examples", | |
| "", | |
| "path to function call information (for displaying examples in the documentation)", | |
| ), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "write-doc-meta-dir", | |
| "Writes trait implementations and other info for the current crate to provided path", | |
| "path/to/doc.meta", | |
| ), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "read-doc-meta-dir", | |
| "Includes trait implementations and other crate info from provided path", | |
| "path/to/doc.meta", | |
| ), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "parts-out-dir", | |
| "Deprecated synonym of write-doc-meta-dir", | |
| "path/to/doc.meta", | |
| ), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "include-parts-dir", | |
| "Deprecated synonym of read-doc-meta-dir", | |
| "path/to/doc.meta", | |
| ), | |
| opt( | |
| Unstable, | |
| Opt, | |
| "", | |
| "merge", | |
| "Deprecated option to specify read/write-doc-meta-dir mode", | |
| "none, shared, finalize", | |
| ), | |
| opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""), | |
| opt( | |
| Unstable, | |
| Multi, | |
| "", | |
| "doctest-build-arg", | |
| "One argument (of possibly many) to be used when compiling doctests", | |
| "ARG", | |
| ), | |
| opt( | |
| Unstable, | |
| FlagMulti, | |
| "", | |
| "disable-minification", | |
| "disable the minification of CSS/JS files (perma-unstable, do not use with cached files)", | |
| "", | |
| ), | |
| opt( | |
| Unstable, | |
| Flag, | |
| "", | |
| "generate-macro-expansion", | |
| "Add possibility to expand macros in the HTML source code pages", | |
| "", | |
| ), | |
| // deprecated / removed options | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "plugin-path", | |
| "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information", | |
| "DIR", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "passes", | |
| "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information", | |
| "PASSES", | |
| ), | |
| opt( | |
| Stable, | |
| Multi, | |
| "", | |
| "plugins", | |
| "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information", | |
| "PLUGINS", | |
| ), | |
| opt( | |
| Stable, | |
| FlagMulti, | |
| "", | |
| "no-defaults", | |
| "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information", | |
| "", | |
| ), | |
| opt( | |
| Stable, | |
| Opt, | |
| "r", | |
| "input-format", | |
| "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information", | |
| "[rust]", | |
| ), | |
| ] | |
| } | |
| fn usage(argv0: &str) { | |
| let mut options = getopts::Options::new(); | |
| for option in opts() { | |
| option.apply(&mut options); | |
| } | |
| println!("{}", options.usage(&format!("{argv0} [options] <input>"))); | |
| println!(" @path Read newline separated options from `path`\n"); | |
| println!( | |
| "More information available at {DOC_RUST_LANG_ORG_VERSION}/rustdoc/what-is-rustdoc.html", | |
| ); | |
| } | |
| pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) { | |
| match res { | |
| Ok(()) => dcx.abort_if_errors(), | |
| Err(err) => dcx.fatal(err), | |
| } | |
| } | |
| fn run_renderer< | |
| 'tcx, | |
| T: formats::FormatRenderer<'tcx>, | |
| F: FnOnce( | |
| clean::Crate, | |
| config::RenderOptions, | |
| Cache, | |
| TyCtxt<'tcx>, | |
| ) -> Result<(T, clean::Crate), Error>, | |
| >( | |
| krate: clean::Crate, | |
| renderopts: config::RenderOptions, | |
| cache: formats::cache::Cache, | |
| tcx: TyCtxt<'tcx>, | |
| init: F, | |
| ) { | |
| match formats::run_format::<T, F>(krate, renderopts, cache, tcx, init) { | |
| Ok(_) => tcx.dcx().abort_if_errors(), | |
| Err(e) => { | |
| let mut msg = | |
| tcx.dcx().struct_fatal(format!("couldn't generate documentation: {}", e.error)); | |
| let file = e.file.display().to_string(); | |
| if !file.is_empty() { | |
| msg.note(format!("failed to create or modify {e}")); | |
| } else { | |
| msg.note(format!("failed to create or modify file: {e}")); | |
| } | |
| msg.emit(); | |
| } | |
| } | |
| } | |
| /// Renders and writes cross-crate info files, like the search index. This function exists so that | |
| /// we can run rustdoc without a crate root in the `--merge=finalize` mode. Cross-crate info files | |
| /// discovered via `--read-doc-meta-dir` are combined and written to the doc root. | |
| fn run_merge_finalize(opt: config::RenderOptions) -> Result<(), error::Error> { | |
| assert!( | |
| opt.should_merge.write_rendered_cci, | |
| "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize" | |
| ); | |
| assert!( | |
| !opt.should_merge.read_rendered_cci, | |
| "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize" | |
| ); | |
| let crates = html::render::CrateInfo::read_many(&opt.include_parts_dir)?; | |
| let include_sources = !opt.html_no_source; | |
| html::render::write_not_crate_specific( | |
| &crates, | |
| &opt.output, | |
| &opt, | |
| &opt.themes, | |
| opt.extension_css.as_deref(), | |
| &opt.resource_suffix, | |
| include_sources, | |
| )?; | |
| Ok(()) | |
| } | |
| fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { | |
| // Throw away the first argument, the name of the binary. | |
| // In case of at_args being empty, as might be the case by | |
| // passing empty argument array to execve under some platforms, | |
| // just use an empty slice. | |
| // | |
| // This situation was possible before due to arg_expand_all being | |
| // called before removing the argument, enabling a crash by calling | |
| // the compiler with @empty_file as argv[0] and no more arguments. | |
| let at_args = at_args.get(1..).unwrap_or_default(); | |
| let args = rustc_driver::args::arg_expand_all(early_dcx, at_args); | |
| let mut options = getopts::Options::new(); | |
| for option in opts() { | |
| option.apply(&mut options); | |
| } | |
| let matches = match options.parse(&args) { | |
| Ok(m) => m, | |
| Err(err) => { | |
| early_dcx.early_fatal(err.to_string()); | |
| } | |
| }; | |
| // Note that we discard any distinction between different non-zero exit | |
| // codes from `from_matches` here. | |
| let (input, options, render_options, loaded_paths) = | |
| match config::Options::from_matches(early_dcx, &matches, args) { | |
| Some(opts) => opts, | |
| None => return, | |
| }; | |
| let dcx = | |
| core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts); | |
| let dcx = dcx.handle(); | |
| let input = match input { | |
| config::InputMode::HasFile(input) => input, | |
| config::InputMode::NoInputMergeFinalize => { | |
| return wrap_return( | |
| dcx, | |
| rustc_span::create_session_globals_then(options.edition, &[], None, || { | |
| run_merge_finalize(render_options) | |
| .map_err(|e| format!("could not write merged cross-crate info: {e}")) | |
| }), | |
| ); | |
| } | |
| }; | |
| let output_format = options.output_format; | |
| match ( | |
| options.should_test || output_format == config::OutputFormat::Doctest, | |
| config::markdown_input(&input), | |
| ) { | |
| (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(&input, options, dcx)), | |
| (true, None) => return doctest::run(dcx, input, options), | |
| (false, Some(md_input)) => { | |
| let md_input = md_input.to_owned(); | |
| let edition = options.edition; | |
| let config = core::create_config(input, options, &render_options); | |
| // `markdown::render` can invoke `doctest::make_test`, which | |
| // requires session globals and a thread pool, so we use | |
| // `run_compiler`. | |
| return wrap_return( | |
| dcx, | |
| interface::run_compiler(config, |compiler| { | |
| // construct a phony "crate" without actually running the parser | |
| // allows us to use other compiler infrastructure like dep-info | |
| let file = | |
| compiler.sess.source_map().load_file(&md_input).map_err(|e| { | |
| format!("{md_input}: {e}", md_input = md_input.display()) | |
| })?; | |
| let inner_span = Span::new( | |
| file.start_pos, | |
| BytePos(file.start_pos.0 + file.normalized_source_len.0), | |
| SyntaxContext::root(), | |
| None, | |
| ); | |
| let krate = ast::Crate { | |
| attrs: Default::default(), | |
| items: Default::default(), | |
| spans: ast::ModSpans { inner_span, ..Default::default() }, | |
| id: ast::DUMMY_NODE_ID, | |
| is_placeholder: false, | |
| }; | |
| rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { | |
| let has_dep_info = render_options.dep_info().is_some(); | |
| if render_options.emit.contains(&EmitType::HtmlNonStaticFiles) { | |
| markdown::render_and_write(file, render_options, edition)?; | |
| } | |
| if has_dep_info { | |
| // Register the loaded external files in the source map so they show up in depinfo. | |
| // We can't load them via the source map because it gets created after we process the options. | |
| for external_path in &loaded_paths { | |
| let _ = compiler.sess.source_map().load_binary_file(external_path); | |
| } | |
| rustc_interface::passes::write_dep_info(tcx); | |
| } | |
| Ok(()) | |
| }) | |
| }), | |
| ); | |
| } | |
| (false, None) => {} | |
| } | |
| // need to move these items separately because we lose them by the time the closure is called, | |
| // but we can't create the dcx ahead of time because it's not Send | |
| let show_coverage = options.show_coverage; | |
| let run_check = options.run_check; | |
| // First, parse the crate and extract all relevant information. | |
| info!("starting to run rustc"); | |
| // Interpret the input file as a rust source file, passing it through the | |
| // compiler all the way through the analysis passes. The rustdoc output is | |
| // then generated from the cleaned AST of the crate. This runs all the | |
| // plug/cleaning passes. | |
| let crate_version = options.crate_version.clone(); | |
| let scrape_examples_options = options.scrape_examples_options.clone(); | |
| let bin_crate = options.bin_crate; | |
| let output_format = options.output_format; | |
| let config = core::create_config(input, options, &render_options); | |
| let registered_lints = config.register_lints.is_some(); | |
| interface::run_compiler(config, |compiler| { | |
| let sess = &compiler.sess; | |
| // Register the loaded external files in the source map so they show up in depinfo. | |
| // We can't load them via the source map because it gets created after we process the options. | |
| for external_path in &loaded_paths { | |
| let _ = sess.source_map().load_binary_file(external_path); | |
| } | |
| if sess.opts.describe_lints { | |
| rustc_driver::describe_lints(sess, registered_lints); | |
| return; | |
| } | |
| let krate = rustc_interface::passes::parse(sess); | |
| rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { | |
| if sess.dcx().has_errors().is_some() { | |
| sess.dcx().fatal("Compilation failed, aborting rustdoc"); | |
| } | |
| let (krate, render_opts, mut cache, expanded_macros) = sess | |
| .time("run_global_ctxt", || { | |
| core::run_global_ctxt(tcx, show_coverage, render_options, output_format) | |
| }); | |
| info!("finished with rustc"); | |
| if let Some(options) = scrape_examples_options { | |
| return scrape_examples::run(krate, render_opts, cache, tcx, options, bin_crate); | |
| } | |
| if show_coverage { | |
| // if we ran coverage, bail early, we don't need to also generate docs at this point | |
| // (also we didn't load in any of the useful passes) | |
| return; | |
| } | |
| cache.crate_version = crate_version; | |
| rustc_interface::passes::emit_delayed_lints(tcx); | |
| if render_opts.dep_info().is_some() { | |
| rustc_interface::passes::write_dep_info(tcx); | |
| } | |
| if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir { | |
| dump_feature_usage_metrics(tcx, metrics_dir); | |
| } | |
| if run_check { | |
| // Since we're in "check" mode, no need to generate anything beyond this point. | |
| return; | |
| } | |
| info!("going to format"); | |
| match output_format { | |
| config::OutputFormat::Html => sess.time("render_html", || { | |
| run_renderer( | |
| krate, | |
| render_opts, | |
| cache, | |
| tcx, | |
| |krate, render_opts, cache, tcx| { | |
| html::render::Context::init( | |
| krate, | |
| render_opts, | |
| cache, | |
| tcx, | |
| expanded_macros, | |
| ) | |
| }, | |
| ) | |
| }), | |
| config::OutputFormat::IrJson => sess.time("render_json", || { | |
| run_renderer(krate, render_opts, cache, tcx, json::JsonRenderer::init) | |
| }), | |
| // Already handled above with doctest runners or coverage early return | |
| config::OutputFormat::Doctest | config::OutputFormat::CoverageJson => { | |
| unreachable!() | |
| } | |
| } | |
| }) | |
| }) | |
| } | |
| fn dump_feature_usage_metrics(tcx: TyCtxt<'_>, metrics_dir: &Path) { | |
| let hash = tcx.crate_hash(LOCAL_CRATE); | |
| let crate_name = tcx.crate_name(LOCAL_CRATE); | |
| let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json"); | |
| let metrics_path = metrics_dir.join(metrics_file_name); | |
| if let Err(error) = tcx.features().dump_feature_usage_metrics(metrics_path) { | |
| // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit | |
| // default metrics" to only produce a warning when metrics are enabled by default and emit | |
| // an error only when the user manually enables metrics | |
| tcx.dcx().err(format!("cannot emit feature usage metrics: {error}")); | |
| } | |
| } |