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