Skip to main content

rustdoc/
lib.rs

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