rustdoc/
lib.rs

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