|
// tidy-alphabetical-start |
|
#![cfg_attr(bootstrap, feature(trim_prefix_suffix))] |
|
#![cfg_attr(not(bootstrap), feature(exitcode_exit_method))] |
|
#![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(iter_partition_in_place)] |
|
#![feature(rustc_private)] |
|
#![feature(test)] |
|
#![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_ir; |
|
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_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_structures; |
|
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, Input, 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, |
|
Multi, |
|
"", |
|
"print", |
|
"Rustdoc information to print on stdout (or to a file)", |
|
"<INFO>[=<FILE>]", |
|
), |
|
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( |
|
render_options: config::RenderOptions, |
|
compiler: &interface::Compiler, |
|
) -> Result<(), error::Error> { |
|
assert!( |
|
render_options.should_merge.write_rendered_cci, |
|
"config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize" |
|
); |
|
assert!( |
|
!render_options.should_merge.read_rendered_cci, |
|
"config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize" |
|
); |
|
let crates = html::render::CrateInfo::read_many(&render_options.include_parts_dir)?; |
|
let include_sources = !render_options.html_no_source; |
|
|
|
html::render::write_not_crate_specific( |
|
&crates, |
|
&render_options.output, |
|
&render_options, |
|
&render_options.themes, |
|
render_options.extension_css.as_deref(), |
|
&render_options.resource_suffix, |
|
include_sources, |
|
&crate::html::layout::Layout { |
|
logo: String::new(), |
|
favicon: String::new(), |
|
external_html: render_options.external_html.clone(), |
|
default_settings: render_options.default_settings.clone(), |
|
krate: String::new(), |
|
krate_version: String::new(), |
|
css_file_extension: render_options.extension_css.clone(), |
|
scrape_examples_extension: false, |
|
}, |
|
&compiler.sess, |
|
)?; |
|
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 => { |
|
if !options.prints.is_empty() { |
|
dcx.fatal("`--print` is not supported for the `--write-doc-meta-dir` option"); |
|
} |
|
|
|
let config = core::create_config( |
|
Input::Str { |
|
name: rustc_span::FileName::Custom(String::new()), |
|
input: String::new(), |
|
}, |
|
options, |
|
&render_options, |
|
); |
|
return wrap_return( |
|
dcx, |
|
interface::run_compiler(config, |compiler| { |
|
run_merge_finalize(render_options, compiler) |
|
.map_err(|e| format!("could not write merged cross-crate info: {e}")) |
|
}), |
|
); |
|
} |
|
}; |
|
let md_input = config::markdown_input(&input); |
|
|
|
if options.should_test || options.output_format == config::OutputFormat::Doctest { |
|
if !options.prints.is_empty() { |
|
dcx.fatal(format!( |
|
"`--print` is not yet supported for the `{}` option", |
|
if options.should_test { "--test" } else { "--output-format=doctest" } |
|
)); |
|
} |
|
|
|
return match md_input { |
|
Some(_) => wrap_return(dcx, doctest::test_markdown(&input, options, dcx)), |
|
None => doctest::run(dcx, input, options), |
|
}; |
|
} |
|
|
|
if let Some(md_input) = md_input { |
|
if !options.prints.is_empty() { |
|
dcx.fatal("`--print` is not yet supported for standalone Markdown files"); |
|
} |
|
|
|
return { |
|
let md_input = md_input.to_owned(); |
|
let edition = options.edition; |
|
let config = core::create_config(input, options, &render_options); |
|
let registered_lints = config.register_lints.is_some(); |
|
|
|
// `markdown::render` can invoke `doctest::make_test`, which |
|
// requires session globals and a thread pool, so we use |
|
// `run_compiler`. |
|
wrap_return( |
|
dcx, |
|
interface::run_compiler(config, |compiler| { |
|
let sess = &compiler.sess; |
|
|
|
// -W help |
|
if sess.opts.describe_lints { |
|
rustc_driver::describe_lints(sess, registered_lints); |
|
return Ok(()); |
|
} |
|
|
|
// construct a phony "crate" without actually running the parser |
|
// allows us to use other compiler infrastructure like dep-info |
|
let file = 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, |
|
}; |
|
let (res, _incr_comp_session) = |
|
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(()) |
|
}); |
|
res |
|
}), |
|
) |
|
}; |
|
} |
|
|
|
// 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); |
|
} |
|
|
|
// -W help |
|
if sess.opts.describe_lints { |
|
rustc_driver::describe_lints(sess, registered_lints); |
|
return; |
|
} |
|
|
|
// --print |
|
if rustc_driver::print_crate_info(&*compiler.codegen_backend, sess, true) |
|
== rustc_driver::Compilation::Stop |
|
{ |
|
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}")); |
|
} |
|
} |