Skip to main content

rustdoc/
lib.rs

1// tidy-alphabetical-start
2#![doc(
3    html_root_url = "https://doc.rust-lang.org/nightly/",
4    html_playground_url = "https://play.rust-lang.org/"
5)]
6#![feature(ascii_char)]
7#![feature(ascii_char_variants)]
8#![feature(assert_matches)]
9#![feature(box_into_inner)]
10#![feature(box_patterns)]
11#![feature(file_buffered)]
12#![feature(formatting_options)]
13#![feature(if_let_guard)]
14#![feature(iter_advance_by)]
15#![feature(iter_intersperse)]
16#![feature(iter_order_by)]
17#![feature(rustc_private)]
18#![feature(test)]
19#![feature(trim_prefix_suffix)]
20#![warn(rustc::internal)]
21// tidy-alphabetical-end
22
23// N.B. these need `extern crate` even in 2018 edition
24// because they're loaded implicitly from the sysroot.
25// The reason they're loaded from the sysroot is because
26// the rustdoc artifacts aren't stored in rustc's cargo target directory.
27// So if `rustc` was specified in Cargo.toml, this would spuriously rebuild crates.
28//
29// Dependencies listed in Cargo.toml do not need `extern crate`.
30
31extern crate rustc_abi;
32extern crate rustc_ast;
33extern crate rustc_ast_pretty;
34extern crate rustc_attr_parsing;
35extern crate rustc_data_structures;
36extern crate rustc_driver;
37extern crate rustc_errors;
38extern crate rustc_feature;
39extern crate rustc_hir;
40extern crate rustc_hir_analysis;
41extern crate rustc_hir_pretty;
42extern crate rustc_index;
43extern crate rustc_infer;
44extern crate rustc_interface;
45extern crate rustc_lexer;
46extern crate rustc_lint;
47extern crate rustc_lint_defs;
48extern crate rustc_log;
49extern crate rustc_macros;
50extern crate rustc_metadata;
51extern crate rustc_middle;
52extern crate rustc_parse;
53extern crate rustc_passes;
54extern crate rustc_resolve;
55extern crate rustc_serialize;
56extern crate rustc_session;
57extern crate rustc_span;
58extern crate rustc_target;
59extern crate rustc_trait_selection;
60extern crate test;
61
62/// See docs in https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs
63/// and https://github.com/rust-lang/rust/pull/146627 for why we need this.
64///
65/// FIXME(madsmtm): This is loaded from the sysroot that was built with the other `rustc` crates
66/// above, instead of via Cargo as you'd normally do. This is currently needed for LTO due to
67/// https://github.com/rust-lang/cc-rs/issues/1613.
68#[cfg(feature = "jemalloc")]
69extern crate tikv_jemalloc_sys as _;
70
71use std::env::{self, VarError};
72use std::io::{self, IsTerminal};
73use std::path::Path;
74use std::process::ExitCode;
75
76use rustc_errors::DiagCtxtHandle;
77use rustc_hir::def_id::LOCAL_CRATE;
78use rustc_hir::lints::DelayedLint;
79use rustc_interface::interface;
80use rustc_middle::ty::TyCtxt;
81use rustc_session::config::{ErrorOutputType, RustcOptGroup, make_crate_type_option};
82use rustc_session::{EarlyDiagCtxt, getopts};
83use tracing::info;
84
85use crate::clean::utils::DOC_RUST_LANG_ORG_VERSION;
86use crate::error::Error;
87use crate::formats::cache::Cache;
88
89/// A macro to create a FxHashMap.
90///
91/// Example:
92///
93/// ```ignore(cannot-test-this-because-non-exported-macro)
94/// let letters = map!{"a" => "b", "c" => "d"};
95/// ```
96///
97/// Trailing commas are allowed.
98/// Commas between elements are required (even if the expression is a block).
99macro_rules! map {
100    ($( $key: expr => $val: expr ),* $(,)*) => {{
101        let mut map = ::rustc_data_structures::fx::FxIndexMap::default();
102        $( map.insert($key, $val); )*
103        map
104    }}
105}
106
107mod clean;
108mod config;
109mod core;
110mod display;
111mod docfs;
112mod doctest;
113mod error;
114mod externalfiles;
115mod fold;
116mod formats;
117// used by the error-index generator, so it needs to be public
118pub mod html;
119mod json;
120pub(crate) mod lint;
121mod markdown;
122mod passes;
123mod scrape_examples;
124mod theme;
125mod visit;
126mod visit_ast;
127mod visit_lib;
128
129pub fn main() -> ExitCode {
130    let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
131
132    rustc_driver::install_ice_hook(
133        "https://github.com/rust-lang/rust/issues/new\
134    ?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md",
135        |_| (),
136    );
137
138    // When using CI artifacts with `download-rustc`, tracing is unconditionally built
139    // with `--features=static_max_level_info`, which disables almost all rustdoc logging. To avoid
140    // this, compile our own version of `tracing` that logs all levels.
141    // NOTE: this compiles both versions of tracing unconditionally, because
142    // - The compile time hit is not that bad, especially compared to rustdoc's incremental times, and
143    // - Otherwise, there's no warning that logging is being ignored when `download-rustc` is enabled
144
145    crate::init_logging(&early_dcx);
146    match rustc_log::init_logger(rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")) {
147        Ok(()) => {}
148        // With `download-rustc = true` there are definitely 2 distinct tracing crates in the
149        // dependency graph: one in the downloaded sysroot and one built just now as a dependency of
150        // rustdoc. So the sysroot's tracing is definitely not yet initialized here.
151        //
152        // But otherwise, depending on link style, there may or may not be 2 tracing crates in play.
153        // The one we just initialized in `crate::init_logging` above is rustdoc's direct dependency
154        // on tracing. When rustdoc is built by x.py using Cargo, rustc_driver's and rustc_log's
155        // tracing dependency is distinct from this one and also needs to be initialized (using the
156        // same RUSTDOC_LOG environment variable for both). Other build systems may use just a
157        // single tracing crate throughout the rustc and rustdoc build.
158        //
159        // The reason initializing 2 tracings does not show double logging when `download-rustc =
160        // false` and `debug_logging = true` is because all rustc logging goes only to its version
161        // of tracing (the one in the sysroot) and all of rustdoc's logging only goes to its version
162        // (the one in Cargo.toml).
163        Err(rustc_log::Error::AlreadyInit(_)) => {}
164        Err(error) => early_dcx.early_fatal(error.to_string()),
165    }
166
167    rustc_driver::catch_with_exit_code(|| {
168        let at_args = rustc_driver::args::raw_args(&early_dcx);
169        main_args(&mut early_dcx, &at_args);
170    })
171}
172
173fn init_logging(early_dcx: &EarlyDiagCtxt) {
174    let color_logs = match env::var("RUSTDOC_LOG_COLOR").as_deref() {
175        Ok("always") => true,
176        Ok("never") => false,
177        Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(),
178        Ok(value) => early_dcx.early_fatal(format!(
179            "invalid log color value '{value}': expected one of always, never, or auto",
180        )),
181        Err(VarError::NotUnicode(value)) => early_dcx.early_fatal(format!(
182            "invalid log color value '{}': expected one of always, never, or auto",
183            value.to_string_lossy()
184        )),
185    };
186    let filter = tracing_subscriber::EnvFilter::from_env("RUSTDOC_LOG");
187    let layer = tracing_tree::HierarchicalLayer::default()
188        .with_writer(io::stderr)
189        .with_ansi(color_logs)
190        .with_targets(true)
191        .with_wraparound(10)
192        .with_verbose_exit(true)
193        .with_verbose_entry(true)
194        .with_indent_amount(2);
195    #[cfg(debug_assertions)]
196    let layer = layer.with_thread_ids(true).with_thread_names(true);
197
198    use tracing_subscriber::layer::SubscriberExt;
199    let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
200    tracing::subscriber::set_global_default(subscriber).unwrap();
201}
202
203fn opts() -> Vec<RustcOptGroup> {
204    use rustc_session::config::OptionKind::{Flag, FlagMulti, Multi, Opt};
205    use rustc_session::config::OptionStability::{Stable, Unstable};
206    use rustc_session::config::make_opt as opt;
207
208    vec![
209        opt(Stable, FlagMulti, "h", "help", "show this help message", ""),
210        opt(Stable, FlagMulti, "V", "version", "print rustdoc's version", ""),
211        opt(Stable, FlagMulti, "v", "verbose", "use verbose output", ""),
212        opt(Stable, Opt, "w", "output-format", "the output type to write", "[html]"),
213        opt(
214            Stable,
215            Opt,
216            "",
217            "output",
218            "Which directory to place the output. This option is deprecated, use --out-dir instead.",
219            "PATH",
220        ),
221        opt(Stable, Opt, "o", "out-dir", "which directory to place the output", "PATH"),
222        opt(Stable, Opt, "", "crate-name", "specify the name of this crate", "NAME"),
223        make_crate_type_option(),
224        opt(Stable, Multi, "L", "library-path", "directory to add to crate search path", "DIR"),
225        opt(Stable, Multi, "", "cfg", "pass a --cfg to rustc", ""),
226        opt(Stable, Multi, "", "check-cfg", "pass a --check-cfg to rustc", ""),
227        opt(Stable, Multi, "", "extern", "pass an --extern to rustc", "NAME[=PATH]"),
228        opt(
229            Unstable,
230            Multi,
231            "",
232            "extern-html-root-url",
233            "base URL to use for dependencies; for example, \
234                \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html",
235            "NAME=URL",
236        ),
237        opt(
238            Unstable,
239            FlagMulti,
240            "",
241            "extern-html-root-takes-precedence",
242            "give precedence to `--extern-html-root-url`, not `html_root_url`",
243            "",
244        ),
245        opt(Stable, Multi, "C", "codegen", "pass a codegen option to rustc", "OPT[=VALUE]"),
246        opt(Stable, FlagMulti, "", "document-private-items", "document private items", ""),
247        opt(
248            Unstable,
249            FlagMulti,
250            "",
251            "document-hidden-items",
252            "document items that have doc(hidden)",
253            "",
254        ),
255        opt(Stable, FlagMulti, "", "test", "run code examples as tests", ""),
256        opt(Stable, Multi, "", "test-args", "arguments to pass to the test runner", "ARGS"),
257        opt(
258            Stable,
259            Opt,
260            "",
261            "test-run-directory",
262            "The working directory in which to run tests",
263            "PATH",
264        ),
265        opt(Stable, Opt, "", "target", "target triple to document", "TRIPLE"),
266        opt(
267            Stable,
268            Multi,
269            "",
270            "markdown-css",
271            "CSS files to include via <link> in a rendered Markdown file",
272            "FILES",
273        ),
274        opt(
275            Stable,
276            Multi,
277            "",
278            "html-in-header",
279            "files to include inline in the <head> section of a rendered Markdown file \
280                or generated documentation",
281            "FILES",
282        ),
283        opt(
284            Stable,
285            Multi,
286            "",
287            "html-before-content",
288            "files to include inline between <body> and the content of a rendered \
289                Markdown file or generated documentation",
290            "FILES",
291        ),
292        opt(
293            Stable,
294            Multi,
295            "",
296            "html-after-content",
297            "files to include inline between the content and </body> of a rendered \
298                Markdown file or generated documentation",
299            "FILES",
300        ),
301        opt(
302            Unstable,
303            Multi,
304            "",
305            "markdown-before-content",
306            "files to include inline between <body> and the content of a rendered \
307                Markdown file or generated documentation",
308            "FILES",
309        ),
310        opt(
311            Unstable,
312            Multi,
313            "",
314            "markdown-after-content",
315            "files to include inline between the content and </body> of a rendered \
316                Markdown file or generated documentation",
317            "FILES",
318        ),
319        opt(Stable, Opt, "", "markdown-playground-url", "URL to send code snippets to", "URL"),
320        opt(Stable, FlagMulti, "", "markdown-no-toc", "don't include table of contents", ""),
321        opt(
322            Stable,
323            Opt,
324            "e",
325            "extend-css",
326            "To add some CSS rules with a given file to generate doc with your own theme. \
327                However, your theme might break if the rustdoc's generated HTML changes, so be careful!",
328            "PATH",
329        ),
330        opt(
331            Unstable,
332            Multi,
333            "Z",
334            "",
335            "unstable / perma-unstable options (only on nightly build)",
336            "FLAG",
337        ),
338        opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"),
339        opt(
340            Unstable,
341            Opt,
342            "",
343            "playground-url",
344            "URL to send code snippets to, may be reset by --markdown-playground-url \
345                or `#![doc(html_playground_url=...)]`",
346            "URL",
347        ),
348        opt(
349            Unstable,
350            FlagMulti,
351            "",
352            "display-doctest-warnings",
353            "show warnings that originate in doctests",
354            "",
355        ),
356        opt(
357            Stable,
358            Opt,
359            "",
360            "crate-version",
361            "crate version to print into documentation",
362            "VERSION",
363        ),
364        opt(
365            Unstable,
366            FlagMulti,
367            "",
368            "sort-modules-by-appearance",
369            "sort modules by where they appear in the program, rather than alphabetically",
370            "",
371        ),
372        opt(
373            Stable,
374            Opt,
375            "",
376            "default-theme",
377            "Set the default theme. THEME should be the theme name, generally lowercase. \
378                If an unknown default theme is specified, the builtin default is used. \
379                The set of themes, and the rustdoc built-in default, are not stable.",
380            "THEME",
381        ),
382        opt(
383            Unstable,
384            Multi,
385            "",
386            "default-setting",
387            "Default value for a rustdoc setting (used when \"rustdoc-SETTING\" is absent \
388                from web browser Local Storage). If VALUE is not supplied, \"true\" is used. \
389                Supported SETTINGs and VALUEs are not documented and not stable.",
390            "SETTING[=VALUE]",
391        ),
392        opt(
393            Stable,
394            Multi,
395            "",
396            "theme",
397            "additional themes which will be added to the generated docs",
398            "FILES",
399        ),
400        opt(Stable, Multi, "", "check-theme", "check if given theme is valid", "FILES"),
401        opt(
402            Unstable,
403            Opt,
404            "",
405            "resource-suffix",
406            "suffix to add to CSS and JavaScript files, \
407                e.g., \"search-index.js\" will become \"search-index-suffix.js\"",
408            "PATH",
409        ),
410        opt(
411            Stable,
412            Opt,
413            "",
414            "edition",
415            "edition to use when compiling rust code (default: 2015)",
416            "EDITION",
417        ),
418        opt(
419            Stable,
420            Opt,
421            "",
422            "color",
423            "Configure coloring of output:
424                                          auto   = colorize, if output goes to a tty (default);
425                                          always = always colorize output;
426                                          never  = never colorize output",
427            "auto|always|never",
428        ),
429        opt(
430            Stable,
431            Opt,
432            "",
433            "error-format",
434            "How errors and other messages are produced",
435            "human|json|short",
436        ),
437        opt(
438            Stable,
439            Opt,
440            "",
441            "diagnostic-width",
442            "Provide width of the output for truncated error messages",
443            "WIDTH",
444        ),
445        opt(Stable, Opt, "", "json", "Configure the structure of JSON diagnostics", "CONFIG"),
446        opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"),
447        opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"),
448        opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"),
449        opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"),
450        opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"),
451        opt(
452            Stable,
453            Multi,
454            "",
455            "cap-lints",
456            "Set the most restrictive lint level. \
457                More restrictive lints are capped at this level. \
458                By default, it is at `forbid` level.",
459            "LEVEL",
460        ),
461        opt(Unstable, Opt, "", "index-page", "Markdown file to be used as index page", "PATH"),
462        opt(
463            Unstable,
464            FlagMulti,
465            "",
466            "enable-index-page",
467            "To enable generation of the index page",
468            "",
469        ),
470        opt(
471            Unstable,
472            Opt,
473            "",
474            "static-root-path",
475            "Path string to force loading static files from in output pages. \
476                If not set, uses combinations of '../' to reach the documentation root.",
477            "PATH",
478        ),
479        opt(
480            Unstable,
481            Opt,
482            "",
483            "persist-doctests",
484            "Directory to persist doctest executables into",
485            "PATH",
486        ),
487        opt(
488            Unstable,
489            FlagMulti,
490            "",
491            "show-coverage",
492            "calculate percentage of public items with documentation",
493            "",
494        ),
495        opt(
496            Stable,
497            Opt,
498            "",
499            "test-runtool",
500            "",
501            "The tool to run tests with when building for a different target than host",
502        ),
503        opt(
504            Stable,
505            Multi,
506            "",
507            "test-runtool-arg",
508            "",
509            "One argument (of possibly many) to pass to the runtool",
510        ),
511        opt(
512            Unstable,
513            Opt,
514            "",
515            "test-builder",
516            "The rustc-like binary to use as the test builder",
517            "PATH",
518        ),
519        opt(
520            Unstable,
521            Multi,
522            "",
523            "test-builder-wrapper",
524            "Wrapper program to pass test-builder and arguments",
525            "PATH",
526        ),
527        opt(Unstable, FlagMulti, "", "check", "Run rustdoc checks", ""),
528        opt(
529            Unstable,
530            FlagMulti,
531            "",
532            "generate-redirect-map",
533            "Generate JSON file at the top level instead of generating HTML redirection files",
534            "",
535        ),
536        opt(
537            Unstable,
538            Multi,
539            "",
540            "emit",
541            "Comma separated list of types of output for rustdoc to emit",
542            "[toolchain-shared-resources,invocation-specific,dep-info]",
543        ),
544        opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""),
545        opt(
546            Unstable,
547            Opt,
548            "",
549            "merge-doctests",
550            "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.",
551            "yes|no|auto",
552        ),
553        opt(
554            Unstable,
555            Multi,
556            "",
557            "remap-path-prefix",
558            "Remap source names in compiler messages",
559            "FROM=TO",
560        ),
561        opt(
562            Unstable,
563            FlagMulti,
564            "",
565            "show-type-layout",
566            "Include the memory layout of types in the docs",
567            "",
568        ),
569        opt(Unstable, Flag, "", "no-capture", "Don't capture stdout and stderr of tests", ""),
570        opt(
571            Unstable,
572            Flag,
573            "",
574            "generate-link-to-definition",
575            "Make the identifiers in the HTML source code pages navigable",
576            "",
577        ),
578        opt(
579            Unstable,
580            Opt,
581            "",
582            "scrape-examples-output-path",
583            "",
584            "collect function call information and output at the given path",
585        ),
586        opt(
587            Unstable,
588            Multi,
589            "",
590            "scrape-examples-target-crate",
591            "",
592            "collect function call information for functions from the target crate",
593        ),
594        opt(Unstable, Flag, "", "scrape-tests", "Include test code when scraping examples", ""),
595        opt(
596            Unstable,
597            Multi,
598            "",
599            "with-examples",
600            "",
601            "path to function call information (for displaying examples in the documentation)",
602        ),
603        opt(
604            Unstable,
605            Opt,
606            "",
607            "merge",
608            "Controls how rustdoc handles files from previously documented crates in the doc root\n\
609                none = Do not write cross-crate information to the --out-dir\n\
610                shared = Append current crate's info to files found in the --out-dir\n\
611                finalize = Write current crate's info and --include-parts-dir info to the --out-dir, overwriting conflicting files",
612            "none|shared|finalize",
613        ),
614        opt(
615            Unstable,
616            Opt,
617            "",
618            "parts-out-dir",
619            "Writes trait implementations and other info for the current crate to provided path. Only use with --merge=none",
620            "path/to/doc.parts/<crate-name>",
621        ),
622        opt(
623            Unstable,
624            Multi,
625            "",
626            "include-parts-dir",
627            "Includes trait implementations and other crate info from provided path. Only use with --merge=finalize",
628            "path/to/doc.parts/<crate-name>",
629        ),
630        opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""),
631        opt(
632            Unstable,
633            Multi,
634            "",
635            "doctest-build-arg",
636            "One argument (of possibly many) to be used when compiling doctests",
637            "ARG",
638        ),
639        opt(
640            Unstable,
641            FlagMulti,
642            "",
643            "disable-minification",
644            "disable the minification of CSS/JS files (perma-unstable, do not use with cached files)",
645            "",
646        ),
647        opt(
648            Unstable,
649            Flag,
650            "",
651            "generate-macro-expansion",
652            "Add possibility to expand macros in the HTML source code pages",
653            "",
654        ),
655        // deprecated / removed options
656        opt(
657            Stable,
658            Multi,
659            "",
660            "plugin-path",
661            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
662            "DIR",
663        ),
664        opt(
665            Stable,
666            Multi,
667            "",
668            "passes",
669            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
670            "PASSES",
671        ),
672        opt(
673            Stable,
674            Multi,
675            "",
676            "plugins",
677            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
678            "PLUGINS",
679        ),
680        opt(
681            Stable,
682            FlagMulti,
683            "",
684            "no-defaults",
685            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
686            "",
687        ),
688        opt(
689            Stable,
690            Opt,
691            "r",
692            "input-format",
693            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
694            "[rust]",
695        ),
696    ]
697}
698
699fn usage(argv0: &str) {
700    let mut options = getopts::Options::new();
701    for option in opts() {
702        option.apply(&mut options);
703    }
704    println!("{}", options.usage(&format!("{argv0} [options] <input>")));
705    println!("    @path               Read newline separated options from `path`\n");
706    println!(
707        "More information available at {DOC_RUST_LANG_ORG_VERSION}/rustdoc/what-is-rustdoc.html",
708    );
709}
710
711pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) {
712    match res {
713        Ok(()) => dcx.abort_if_errors(),
714        Err(err) => dcx.fatal(err),
715    }
716}
717
718fn run_renderer<
719    'tcx,
720    T: formats::FormatRenderer<'tcx>,
721    F: FnOnce(
722        clean::Crate,
723        config::RenderOptions,
724        Cache,
725        TyCtxt<'tcx>,
726    ) -> Result<(T, clean::Crate), Error>,
727>(
728    krate: clean::Crate,
729    renderopts: config::RenderOptions,
730    cache: formats::cache::Cache,
731    tcx: TyCtxt<'tcx>,
732    init: F,
733) {
734    match formats::run_format::<T, F>(krate, renderopts, cache, tcx, init) {
735        Ok(_) => tcx.dcx().abort_if_errors(),
736        Err(e) => {
737            let mut msg =
738                tcx.dcx().struct_fatal(format!("couldn't generate documentation: {}", e.error));
739            let file = e.file.display().to_string();
740            if !file.is_empty() {
741                msg.note(format!("failed to create or modify {e}"));
742            } else {
743                msg.note(format!("failed to create or modify file: {e}"));
744            }
745            msg.emit();
746        }
747    }
748}
749
750/// Renders and writes cross-crate info files, like the search index. This function exists so that
751/// we can run rustdoc without a crate root in the `--merge=finalize` mode. Cross-crate info files
752/// discovered via `--include-parts-dir` are combined and written to the doc root.
753fn run_merge_finalize(opt: config::RenderOptions) -> Result<(), error::Error> {
754    assert!(
755        opt.should_merge.write_rendered_cci,
756        "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize"
757    );
758    assert!(
759        !opt.should_merge.read_rendered_cci,
760        "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize"
761    );
762    let crates = html::render::CrateInfo::read_many(&opt.include_parts_dir)?;
763    let include_sources = !opt.html_no_source;
764    html::render::write_not_crate_specific(
765        &crates,
766        &opt.output,
767        &opt,
768        &opt.themes,
769        opt.extension_css.as_deref(),
770        &opt.resource_suffix,
771        include_sources,
772    )?;
773    Ok(())
774}
775
776fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
777    // Throw away the first argument, the name of the binary.
778    // In case of at_args being empty, as might be the case by
779    // passing empty argument array to execve under some platforms,
780    // just use an empty slice.
781    //
782    // This situation was possible before due to arg_expand_all being
783    // called before removing the argument, enabling a crash by calling
784    // the compiler with @empty_file as argv[0] and no more arguments.
785    let at_args = at_args.get(1..).unwrap_or_default();
786
787    let args = rustc_driver::args::arg_expand_all(early_dcx, at_args);
788
789    let mut options = getopts::Options::new();
790    for option in opts() {
791        option.apply(&mut options);
792    }
793    let matches = match options.parse(&args) {
794        Ok(m) => m,
795        Err(err) => {
796            early_dcx.early_fatal(err.to_string());
797        }
798    };
799
800    // Note that we discard any distinction between different non-zero exit
801    // codes from `from_matches` here.
802    let (input, options, render_options, loaded_paths) =
803        match config::Options::from_matches(early_dcx, &matches, args) {
804            Some(opts) => opts,
805            None => return,
806        };
807
808    let dcx =
809        core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts);
810    let dcx = dcx.handle();
811
812    let input = match input {
813        config::InputMode::HasFile(input) => input,
814        config::InputMode::NoInputMergeFinalize => {
815            return wrap_return(
816                dcx,
817                rustc_span::create_session_globals_then(options.edition, &[], None, || {
818                    run_merge_finalize(render_options)
819                        .map_err(|e| format!("could not write merged cross-crate info: {e}"))
820                }),
821            );
822        }
823    };
824
825    let output_format = options.output_format;
826
827    match (
828        options.should_test || output_format == config::OutputFormat::Doctest,
829        config::markdown_input(&input),
830    ) {
831        (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(&input, options, dcx)),
832        (true, None) => return doctest::run(dcx, input, options),
833        (false, Some(md_input)) => {
834            let md_input = md_input.to_owned();
835            let edition = options.edition;
836            let config = core::create_config(input, options, &render_options);
837
838            // `markdown::render` can invoke `doctest::make_test`, which
839            // requires session globals and a thread pool, so we use
840            // `run_compiler`.
841            return wrap_return(
842                dcx,
843                interface::run_compiler(config, |_compiler| {
844                    markdown::render_and_write(&md_input, render_options, edition)
845                }),
846            );
847        }
848        (false, None) => {}
849    }
850
851    // need to move these items separately because we lose them by the time the closure is called,
852    // but we can't create the dcx ahead of time because it's not Send
853    let show_coverage = options.show_coverage;
854    let run_check = options.run_check;
855
856    // First, parse the crate and extract all relevant information.
857    info!("starting to run rustc");
858
859    // Interpret the input file as a rust source file, passing it through the
860    // compiler all the way through the analysis passes. The rustdoc output is
861    // then generated from the cleaned AST of the crate. This runs all the
862    // plug/cleaning passes.
863    let crate_version = options.crate_version.clone();
864
865    let scrape_examples_options = options.scrape_examples_options.clone();
866    let bin_crate = options.bin_crate;
867
868    let output_format = options.output_format;
869    let config = core::create_config(input, options, &render_options);
870
871    let registered_lints = config.register_lints.is_some();
872
873    interface::run_compiler(config, |compiler| {
874        let sess = &compiler.sess;
875
876        // Register the loaded external files in the source map so they show up in depinfo.
877        // We can't load them via the source map because it gets created after we process the options.
878        for external_path in &loaded_paths {
879            let _ = sess.source_map().load_binary_file(external_path);
880        }
881
882        if sess.opts.describe_lints {
883            rustc_driver::describe_lints(sess, registered_lints);
884            return;
885        }
886
887        let krate = rustc_interface::passes::parse(sess);
888        rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| {
889            if sess.dcx().has_errors().is_some() {
890                sess.dcx().fatal("Compilation failed, aborting rustdoc");
891            }
892
893            let (krate, render_opts, mut cache, expanded_macros) = sess
894                .time("run_global_ctxt", || {
895                    core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
896                });
897            info!("finished with rustc");
898
899            if let Some(options) = scrape_examples_options {
900                return scrape_examples::run(krate, render_opts, cache, tcx, options, bin_crate);
901            }
902
903            cache.crate_version = crate_version;
904
905            if show_coverage {
906                // if we ran coverage, bail early, we don't need to also generate docs at this point
907                // (also we didn't load in any of the useful passes)
908                return;
909            }
910
911            for owner_id in tcx.hir_crate_items(()).delayed_lint_items() {
912                if let Some(delayed_lints) = tcx.opt_ast_lowering_delayed_lints(owner_id) {
913                    for lint in &delayed_lints.lints {
914                        match lint {
915                            DelayedLint::AttributeParsing(attribute_lint) => {
916                                tcx.node_span_lint(
917                                    attribute_lint.lint_id.lint,
918                                    attribute_lint.id,
919                                    attribute_lint.span,
920                                    |diag| {
921                                        rustc_lint::decorate_attribute_lint(
922                                            tcx.sess,
923                                            Some(tcx),
924                                            &attribute_lint.kind,
925                                            diag,
926                                        );
927                                    },
928                                );
929                            }
930                        }
931                    }
932                }
933            }
934
935            if render_opts.dep_info().is_some() {
936                rustc_interface::passes::write_dep_info(tcx);
937            }
938
939            if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
940                dump_feature_usage_metrics(tcx, metrics_dir);
941            }
942
943            if run_check {
944                // Since we're in "check" mode, no need to generate anything beyond this point.
945                return;
946            }
947
948            info!("going to format");
949            match output_format {
950                config::OutputFormat::Html => sess.time("render_html", || {
951                    run_renderer(
952                        krate,
953                        render_opts,
954                        cache,
955                        tcx,
956                        |krate, render_opts, cache, tcx| {
957                            html::render::Context::init(
958                                krate,
959                                render_opts,
960                                cache,
961                                tcx,
962                                expanded_macros,
963                            )
964                        },
965                    )
966                }),
967                config::OutputFormat::Json => sess.time("render_json", || {
968                    run_renderer(krate, render_opts, cache, tcx, json::JsonRenderer::init)
969                }),
970                // Already handled above with doctest runners.
971                config::OutputFormat::Doctest => unreachable!(),
972            }
973        })
974    })
975}
976
977fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) {
978    let hash = tcxt.crate_hash(LOCAL_CRATE);
979    let crate_name = tcxt.crate_name(LOCAL_CRATE);
980    let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json");
981    let metrics_path = metrics_dir.join(metrics_file_name);
982    if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) {
983        // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
984        // default metrics" to only produce a warning when metrics are enabled by default and emit
985        // an error only when the user manually enables metrics
986        tcxt.dcx().err(format!("cannot emit feature usage metrics: {error}"));
987    }
988}