Skip to main content

rustdoc/
lib.rs

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