rustdoc/
lib.rs

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