rustdoc/
lib.rs

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