1 use std
::collections
::BTreeMap
;
2 use std
::convert
::TryFrom
;
5 use std
::path
::PathBuf
;
7 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
8 use rustc_hir
::def_id
::DefId
;
9 use rustc_middle
::middle
::privacy
::AccessLevels
;
10 use rustc_session
::config
::{self, parse_crate_types_from_list, parse_externs, CrateType}
;
11 use rustc_session
::config
::{
12 build_codegen_options
, build_debugging_options
, get_cmd_lint_options
, host_triple
,
15 use rustc_session
::config
::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs}
;
16 use rustc_session
::getopts
;
17 use rustc_session
::lint
::Level
;
18 use rustc_session
::search_paths
::SearchPath
;
19 use rustc_span
::edition
::{Edition, DEFAULT_EDITION}
;
20 use rustc_target
::spec
::TargetTriple
;
22 use crate::core
::new_handler
;
23 use crate::externalfiles
::ExternalHtml
;
25 use crate::html
::markdown
::IdMap
;
26 use crate::html
::render
::StylePath
;
27 use crate::html
::static_files
;
29 use crate::passes
::{self, Condition, DefaultPassOption}
;
32 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
33 pub enum OutputFormat
{
39 pub fn is_json(&self) -> bool
{
41 OutputFormat
::Json
=> true,
47 impl TryFrom
<&str> for OutputFormat
{
50 fn try_from(value
: &str) -> Result
<Self, Self::Error
> {
52 "json" => Ok(OutputFormat
::Json
),
53 "html" => Ok(OutputFormat
::Html
),
54 _
=> Err(format
!("unknown output format `{}`", value
)),
59 /// Configuration options for rustdoc.
62 // Basic options / Options passed directly to rustc
63 /// The crate root or Markdown file to load.
65 /// The name of the crate being documented.
66 pub crate_name
: Option
<String
>,
67 /// Whether or not this is a proc-macro crate
68 pub proc_macro_crate
: bool
,
69 /// How to format errors and warnings.
70 pub error_format
: ErrorOutputType
,
71 /// Library search paths to hand to the compiler.
72 pub libs
: Vec
<SearchPath
>,
73 /// Library search paths strings to hand to the compiler.
74 pub lib_strs
: Vec
<String
>,
75 /// The list of external crates to link against.
77 /// The list of external crates strings to link against.
78 pub extern_strs
: Vec
<String
>,
79 /// List of `cfg` flags to hand to the compiler. Always includes `rustdoc`.
80 pub cfgs
: Vec
<String
>,
81 /// Codegen options to hand to the compiler.
82 pub codegen_options
: CodegenOptions
,
83 /// Codegen options strings to hand to the compiler.
84 pub codegen_options_strs
: Vec
<String
>,
85 /// Debugging (`-Z`) options to pass to the compiler.
86 pub debugging_opts
: DebuggingOptions
,
87 /// Debugging (`-Z`) options strings to pass to the compiler.
88 pub debugging_opts_strs
: Vec
<String
>,
89 /// The target used to compile the crate against.
90 pub target
: TargetTriple
,
91 /// Edition used when reading the crate. Defaults to "2015". Also used by default when
92 /// compiling doctests from the crate.
94 /// The path to the sysroot. Used during the compilation process.
95 pub maybe_sysroot
: Option
<PathBuf
>,
96 /// Lint information passed over the command-line.
97 pub lint_opts
: Vec
<(String
, Level
)>,
98 /// Whether to ask rustc to describe the lints it knows. Practically speaking, this will not be
99 /// used, since we abort if we have no input file, but it's included for completeness.
100 pub describe_lints
: bool
,
101 /// What level to cap lints at.
102 pub lint_cap
: Option
<Level
>,
104 // Options specific to running doctests
105 /// Whether we should run doctests instead of generating docs.
106 pub should_test
: bool
,
107 /// List of arguments to pass to the test harness, if running tests.
108 pub test_args
: Vec
<String
>,
109 /// Optional path to persist the doctest executables to, defaults to a
110 /// temporary directory if not set.
111 pub persist_doctests
: Option
<PathBuf
>,
112 /// Runtool to run doctests with
113 pub runtool
: Option
<String
>,
114 /// Arguments to pass to the runtool
115 pub runtool_args
: Vec
<String
>,
116 /// Whether to allow ignoring doctests on a per-target basis
117 /// For example, using ignore-foo to ignore running the doctest on any target that
118 /// contains "foo" as a substring
119 pub enable_per_target_ignores
: bool
,
121 /// The path to a rustc-like binary to build tests with. If not set, we
122 /// default to loading from $sysroot/bin/rustc.
123 pub test_builder
: Option
<PathBuf
>,
125 // Options that affect the documentation process
126 /// The selected default set of passes to use.
128 /// Be aware: This option can come both from the CLI and from crate attributes!
129 pub default_passes
: DefaultPassOption
,
130 /// Any passes manually selected by the user.
132 /// Be aware: This option can come both from the CLI and from crate attributes!
133 pub manual_passes
: Vec
<String
>,
134 /// Whether to display warnings during doc generation or while gathering doctests. By default,
135 /// all non-rustdoc-specific lints are allowed when generating docs.
136 pub display_warnings
: bool
,
137 /// Whether to run the `calculate-doc-coverage` pass, which counts the number of public items
138 /// with and without documentation.
139 pub show_coverage
: bool
,
141 // Options that alter generated documentation pages
142 /// Crate version to note on the sidebar of generated docs.
143 pub crate_version
: Option
<String
>,
144 /// Collected options specific to outputting final pages.
145 pub render_options
: RenderOptions
,
146 /// Output format rendering (used only for "show-coverage" option for the moment)
147 pub output_format
: Option
<OutputFormat
>,
150 impl fmt
::Debug
for Options
{
151 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
152 struct FmtExterns
<'a
>(&'a Externs
);
154 impl<'a
> fmt
::Debug
for FmtExterns
<'a
> {
155 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
156 f
.debug_map().entries(self.0.iter
()).finish()
160 f
.debug_struct("Options")
161 .field("input", &self.input
)
162 .field("crate_name", &self.crate_name
)
163 .field("proc_macro_crate", &self.proc_macro_crate
)
164 .field("error_format", &self.error_format
)
165 .field("libs", &self.libs
)
166 .field("externs", &FmtExterns(&self.externs
))
167 .field("cfgs", &self.cfgs
)
168 .field("codegen_options", &"...")
169 .field("debugging_options", &"...")
170 .field("target", &self.target
)
171 .field("edition", &self.edition
)
172 .field("maybe_sysroot", &self.maybe_sysroot
)
173 .field("lint_opts", &self.lint_opts
)
174 .field("describe_lints", &self.describe_lints
)
175 .field("lint_cap", &self.lint_cap
)
176 .field("should_test", &self.should_test
)
177 .field("test_args", &self.test_args
)
178 .field("persist_doctests", &self.persist_doctests
)
179 .field("default_passes", &self.default_passes
)
180 .field("manual_passes", &self.manual_passes
)
181 .field("display_warnings", &self.display_warnings
)
182 .field("show_coverage", &self.show_coverage
)
183 .field("crate_version", &self.crate_version
)
184 .field("render_options", &self.render_options
)
185 .field("runtool", &self.runtool
)
186 .field("runtool_args", &self.runtool_args
)
187 .field("enable-per-target-ignores", &self.enable_per_target_ignores
)
192 /// Configuration options for the HTML page-creation process.
193 #[derive(Clone, Debug)]
194 pub struct RenderOptions
{
195 /// Output directory to generate docs into. Defaults to `doc`.
197 /// External files to insert into generated pages.
198 pub external_html
: ExternalHtml
,
199 /// A pre-populated `IdMap` with the default headings and any headings added by Markdown files
200 /// processed by `external_html`.
202 /// If present, playground URL to use in the "Run" button added to code samples.
204 /// Be aware: This option can come both from the CLI and from crate attributes!
205 pub playground_url
: Option
<String
>,
206 /// Whether to sort modules alphabetically on a module page instead of using declaration order.
207 /// `true` by default.
209 // FIXME(misdreavus): the flag name is `--sort-modules-by-appearance` but the meaning is
210 // inverted once read.
211 pub sort_modules_alphabetically
: bool
,
212 /// List of themes to extend the docs with. Original argument name is included to assist in
213 /// displaying errors if it fails a theme check.
214 pub themes
: Vec
<StylePath
>,
215 /// If present, CSS file that contains rules to add to the default CSS.
216 pub extension_css
: Option
<PathBuf
>,
217 /// A map of crate names to the URL to use instead of querying the crate's `html_root_url`.
218 pub extern_html_root_urls
: BTreeMap
<String
, String
>,
219 /// If present, suffix added to CSS/JavaScript files when referencing them in generated pages.
220 pub resource_suffix
: String
,
221 /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by
224 // FIXME(misdreavus): the flag name is `--disable-minification` but the meaning is inverted
226 pub enable_minification
: bool
,
227 /// Whether to create an index page in the root of the output directory. If this is true but
228 /// `enable_index_page` is None, generate a static listing of crates instead.
229 pub enable_index_page
: bool
,
230 /// A file to use as the index page at the root of the output directory. Overrides
231 /// `enable_index_page` to be true if set.
232 pub index_page
: Option
<PathBuf
>,
233 /// An optional path to use as the location of static files. If not set, uses combinations of
234 /// `../` to reach the documentation root.
235 pub static_root_path
: Option
<String
>,
237 // Options specific to reading standalone Markdown files
238 /// Whether to generate a table of contents on the output file when reading a standalone
240 pub markdown_no_toc
: bool
,
241 /// Additional CSS files to link in pages generated from standalone Markdown files.
242 pub markdown_css
: Vec
<String
>,
243 /// If present, playground URL to use in the "Run" button added to code samples generated from
244 /// standalone Markdown files. If not present, `playground_url` is used.
245 pub markdown_playground_url
: Option
<String
>,
246 /// If false, the `select` element to have search filtering by crates on rendered docs
247 /// won't be generated.
248 pub generate_search_filter
: bool
,
249 /// Document items that have lower than `pub` visibility.
250 pub document_private
: bool
,
251 /// Document items that have `doc(hidden)`.
252 pub document_hidden
: bool
,
255 /// Temporary storage for data obtained during `RustdocVisitor::clean()`.
256 /// Later on moved into `CACHE_KEY`.
257 #[derive(Default, Clone)]
258 pub struct RenderInfo
{
259 pub inlined
: FxHashSet
<DefId
>,
260 pub external_paths
: crate::core
::ExternalPaths
,
261 pub exact_paths
: FxHashMap
<DefId
, Vec
<String
>>,
262 pub access_levels
: AccessLevels
<DefId
>,
263 pub deref_trait_did
: Option
<DefId
>,
264 pub deref_mut_trait_did
: Option
<DefId
>,
265 pub owned_box_did
: Option
<DefId
>,
266 pub output_format
: Option
<OutputFormat
>,
270 /// Parses the given command-line for options. If an error message or other early-return has
271 /// been printed, returns `Err` with the exit code.
272 pub fn from_matches(matches
: &getopts
::Matches
) -> Result
<Options
, i32> {
273 // Check for unstable options.
274 nightly_options
::check_nightly_options(&matches
, &opts());
276 if matches
.opt_present("h") || matches
.opt_present("help") {
277 crate::usage("rustdoc");
279 } else if matches
.opt_present("version") {
280 rustc_driver
::version("rustdoc", &matches
);
284 if matches
.opt_strs("passes") == ["list"] {
285 println
!("Available passes for running rustdoc:");
286 for pass
in passes
::PASSES
{
287 println
!("{:>20} - {}", pass
.name
, pass
.description
);
289 println
!("\nDefault passes for rustdoc:");
290 for p
in passes
::DEFAULT_PASSES
{
291 print
!("{:>20}", p
.pass
.name
);
292 println_condition(p
.condition
);
295 if nightly_options
::is_nightly_build() {
296 println
!("\nPasses run with `--show-coverage`:");
297 for p
in passes
::COVERAGE_PASSES
{
298 print
!("{:>20}", p
.pass
.name
);
299 println_condition(p
.condition
);
303 fn println_condition(condition
: Condition
) {
306 Always
=> println
!(),
307 WhenDocumentPrivate
=> println
!(" (when --document-private-items)"),
308 WhenNotDocumentPrivate
=> println
!(" (when not --document-private-items)"),
309 WhenNotDocumentHidden
=> println
!(" (when not --document-hidden-items)"),
316 let color
= config
::parse_color(&matches
);
317 let (json_rendered
, _artifacts
) = config
::parse_json(&matches
);
318 let error_format
= config
::parse_error_format(&matches
, color
, json_rendered
);
320 let codegen_options
= build_codegen_options(matches
, error_format
);
321 let debugging_opts
= build_debugging_options(matches
, error_format
);
323 let diag
= new_handler(error_format
, None
, &debugging_opts
);
325 // check for deprecated options
326 check_deprecated_options(&matches
, &diag
);
328 let to_check
= matches
.opt_strs("check-theme");
329 if !to_check
.is_empty() {
330 let paths
= theme
::load_css_paths(static_files
::themes
::LIGHT
.as_bytes());
333 println
!("rustdoc: [check-theme] Starting tests! (Ignoring all other arguments)");
334 for theme_file
in to_check
.iter() {
335 print
!(" - Checking \"{}\"...", theme_file
);
336 let (success
, differences
) = theme
::test_theme_against(theme_file
, &paths
, &diag
);
337 if !differences
.is_empty() || !success
{
340 if !differences
.is_empty() {
341 println
!("{}", differences
.join("\n"));
353 if matches
.free
.is_empty() {
354 diag
.struct_err("missing file operand").emit();
357 if matches
.free
.len() > 1 {
358 diag
.struct_err("too many file operands").emit();
361 let input
= PathBuf
::from(&matches
.free
[0]);
366 .map(|s
| SearchPath
::from_cli_opt(s
, error_format
))
368 let externs
= parse_externs(&matches
, &debugging_opts
, error_format
);
369 let extern_html_root_urls
= match parse_extern_html_roots(&matches
) {
372 diag
.struct_err(err
).emit();
377 let test_args
= matches
.opt_strs("test-args");
378 let test_args
: Vec
<String
> =
379 test_args
.iter().flat_map(|s
| s
.split_whitespace()).map(|s
| s
.to_string()).collect();
381 let should_test
= matches
.opt_present("test");
384 matches
.opt_str("o").map(|s
| PathBuf
::from(&s
)).unwrap_or_else(|| PathBuf
::from("doc"));
385 let cfgs
= matches
.opt_strs("cfg");
387 let extension_css
= matches
.opt_str("e").map(|s
| PathBuf
::from(&s
));
389 if let Some(ref p
) = extension_css
{
391 diag
.struct_err("option --extend-css argument must be a file").emit();
396 let mut themes
= Vec
::new();
397 if matches
.opt_present("theme") {
398 let paths
= theme
::load_css_paths(static_files
::themes
::LIGHT
.as_bytes());
400 for (theme_file
, theme_s
) in
401 matches
.opt_strs("theme").iter().map(|s
| (PathBuf
::from(&s
), s
.to_owned()))
403 if !theme_file
.is_file() {
404 diag
.struct_err(&format
!("invalid argument: \"{}\"", theme_s
))
405 .help("arguments to --theme must be files")
409 if theme_file
.extension() != Some(OsStr
::new("css")) {
410 diag
.struct_err(&format
!("invalid argument: \"{}\"", theme_s
)).emit();
413 let (success
, ret
) = theme
::test_theme_against(&theme_file
, &paths
, &diag
);
415 diag
.struct_err(&format
!("error loading theme file: \"{}\"", theme_s
)).emit();
417 } else if !ret
.is_empty() {
418 diag
.struct_warn(&format
!(
419 "theme file \"{}\" is missing CSS rules from the default theme",
422 .warn("the theme may appear incorrect when loaded")
424 "to see what rules are missing, call `rustdoc --check-theme \"{}\"`",
429 themes
.push(StylePath { path: theme_file, disabled: true }
);
433 let edition
= if let Some(e
) = matches
.opt_str("edition") {
437 diag
.struct_err("could not parse edition").emit();
445 let mut id_map
= html
::markdown
::IdMap
::new();
446 id_map
.populate(html
::render
::initial_ids());
447 let external_html
= match ExternalHtml
::load(
448 &matches
.opt_strs("html-in-header"),
449 &matches
.opt_strs("html-before-content"),
450 &matches
.opt_strs("html-after-content"),
451 &matches
.opt_strs("markdown-before-content"),
452 &matches
.opt_strs("markdown-after-content"),
459 None
=> return Err(3),
462 match matches
.opt_str("r").as_deref() {
463 Some("rust") | None
=> {}
465 diag
.struct_err(&format
!("unknown input format: {}", s
)).emit();
470 let index_page
= matches
.opt_str("index-page").map(|s
| PathBuf
::from(&s
));
471 if let Some(ref index_page
) = index_page
{
472 if !index_page
.is_file() {
473 diag
.struct_err("option `--index-page` argument must be a file").emit();
479 matches
.opt_str("target").map_or(TargetTriple
::from_triple(host_triple()), |target
| {
480 if target
.ends_with(".json") {
481 TargetTriple
::TargetPath(PathBuf
::from(target
))
483 TargetTriple
::TargetTriple(target
)
487 let show_coverage
= matches
.opt_present("show-coverage");
489 let default_passes
= if matches
.opt_present("no-defaults") {
490 passes
::DefaultPassOption
::None
491 } else if show_coverage
{
492 passes
::DefaultPassOption
::Coverage
494 passes
::DefaultPassOption
::Default
496 let manual_passes
= matches
.opt_strs("passes");
498 let crate_types
= match parse_crate_types_from_list(matches
.opt_strs("crate-type")) {
501 diag
.struct_err(&format
!("unknown crate type: {}", e
)).emit();
506 let output_format
= match matches
.opt_str("output-format") {
507 Some(s
) => match OutputFormat
::try_from(s
.as_str()) {
509 if o
.is_json() && !(show_coverage
|| nightly_options
::is_nightly_build()) {
510 diag
.struct_err("json output format isn't supported for doc generation")
513 } else if !o
.is_json() && show_coverage
{
515 "html output format isn't supported for the --show-coverage option",
523 diag
.struct_err(&e
).emit();
529 let crate_name
= matches
.opt_str("crate-name");
530 let proc_macro_crate
= crate_types
.contains(&CrateType
::ProcMacro
);
531 let playground_url
= matches
.opt_str("playground-url");
532 let maybe_sysroot
= matches
.opt_str("sysroot").map(PathBuf
::from
);
533 let display_warnings
= matches
.opt_present("display-warnings");
534 let sort_modules_alphabetically
= !matches
.opt_present("sort-modules-by-appearance");
535 let resource_suffix
= matches
.opt_str("resource-suffix").unwrap_or_default();
536 let enable_minification
= !matches
.opt_present("disable-minification");
537 let markdown_no_toc
= matches
.opt_present("markdown-no-toc");
538 let markdown_css
= matches
.opt_strs("markdown-css");
539 let markdown_playground_url
= matches
.opt_str("markdown-playground-url");
540 let crate_version
= matches
.opt_str("crate-version");
541 let enable_index_page
= matches
.opt_present("enable-index-page") || index_page
.is_some();
542 let static_root_path
= matches
.opt_str("static-root-path");
543 let generate_search_filter
= !matches
.opt_present("disable-per-crate-search");
544 let persist_doctests
= matches
.opt_str("persist-doctests").map(PathBuf
::from
);
545 let test_builder
= matches
.opt_str("test-builder").map(PathBuf
::from
);
546 let codegen_options_strs
= matches
.opt_strs("C");
547 let debugging_opts_strs
= matches
.opt_strs("Z");
548 let lib_strs
= matches
.opt_strs("L");
549 let extern_strs
= matches
.opt_strs("extern");
550 let runtool
= matches
.opt_str("runtool");
551 let runtool_args
= matches
.opt_strs("runtool-arg");
552 let enable_per_target_ignores
= matches
.opt_present("enable-per-target-ignores");
553 let document_private
= matches
.opt_present("document-private-items");
554 let document_hidden
= matches
.opt_present("document-hidden-items");
556 let (lint_opts
, describe_lints
, lint_cap
) = get_cmd_lint_options(matches
, error_format
);
569 codegen_options_strs
,
588 enable_per_target_ignores
,
590 render_options
: RenderOptions
{
595 sort_modules_alphabetically
,
598 extern_html_root_urls
,
606 markdown_playground_url
,
607 generate_search_filter
,
615 /// Returns `true` if the file given as `self.input` is a Markdown file.
616 pub fn markdown_input(&self) -> bool
{
617 self.input
.extension().map_or(false, |e
| e
== "md" || e
== "markdown")
621 /// Prints deprecation warnings for deprecated options
622 fn check_deprecated_options(matches
: &getopts
::Matches
, diag
: &rustc_errors
::Handler
) {
623 let deprecated_flags
= ["input-format", "output-format", "no-defaults", "passes"];
625 for flag
in deprecated_flags
.iter() {
626 if matches
.opt_present(flag
) {
627 if *flag
== "output-format"
628 && (matches
.opt_present("show-coverage") || nightly_options
::is_nightly_build())
633 diag
.struct_warn(&format
!("the '{}' flag is considered deprecated", flag
));
635 "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \
636 for more information",
639 if *flag
== "no-defaults" {
640 err
.help("you may want to use --document-private-items");
647 let removed_flags
= ["plugins", "plugin-path"];
649 for &flag
in removed_flags
.iter() {
650 if matches
.opt_present(flag
) {
651 diag
.struct_warn(&format
!("the '{}' flag no longer functions", flag
))
652 .warn("see CVE-2018-1000622")
658 /// Extracts `--extern-html-root-url` arguments from `matches` and returns a map of crate names to
659 /// the given URLs. If an `--extern-html-root-url` argument was ill-formed, returns an error
660 /// describing the issue.
661 fn parse_extern_html_roots(
662 matches
: &getopts
::Matches
,
663 ) -> Result
<BTreeMap
<String
, String
>, &'
static str> {
664 let mut externs
= BTreeMap
::new();
665 for arg
in &matches
.opt_strs("extern-html-root-url") {
666 let mut parts
= arg
.splitn(2, '
='
);
667 let name
= parts
.next().ok_or("--extern-html-root-url must not be empty")?
;
668 let url
= parts
.next().ok_or("--extern-html-root-url must be of the form name=url")?
;
669 externs
.insert(name
.to_string(), url
.to_string());