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