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_patterns)]
10#![feature(debug_closure_helpers)]
11#![feature(file_buffered)]
12#![feature(format_args_nl)]
13#![feature(if_let_guard)]
14#![feature(iter_intersperse)]
15#![feature(round_char_boundary)]
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 \"{file}\""));
763            }
764            msg.emit();
765        }
766    }
767}
768
769/// Renders and writes cross-crate info files, like the search index. This function exists so that
770/// we can run rustdoc without a crate root in the `--merge=finalize` mode. Cross-crate info files
771/// discovered via `--include-parts-dir` are combined and written to the doc root.
772fn run_merge_finalize(opt: config::RenderOptions) -> Result<(), error::Error> {
773    assert!(
774        opt.should_merge.write_rendered_cci,
775        "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize"
776    );
777    assert!(
778        !opt.should_merge.read_rendered_cci,
779        "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize"
780    );
781    let crates = html::render::CrateInfo::read_many(&opt.include_parts_dir)?;
782    let include_sources = !opt.html_no_source;
783    html::render::write_not_crate_specific(
784        &crates,
785        &opt.output,
786        &opt,
787        &opt.themes,
788        opt.extension_css.as_deref(),
789        &opt.resource_suffix,
790        include_sources,
791    )?;
792    Ok(())
793}
794
795fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
796    // Throw away the first argument, the name of the binary.
797    // In case of at_args being empty, as might be the case by
798    // passing empty argument array to execve under some platforms,
799    // just use an empty slice.
800    //
801    // This situation was possible before due to arg_expand_all being
802    // called before removing the argument, enabling a crash by calling
803    // the compiler with @empty_file as argv[0] and no more arguments.
804    let at_args = at_args.get(1..).unwrap_or_default();
805
806    let args = rustc_driver::args::arg_expand_all(early_dcx, at_args);
807
808    let mut options = getopts::Options::new();
809    for option in opts() {
810        option.apply(&mut options);
811    }
812    let matches = match options.parse(&args) {
813        Ok(m) => m,
814        Err(err) => {
815            early_dcx.early_fatal(err.to_string());
816        }
817    };
818
819    // Note that we discard any distinction between different non-zero exit
820    // codes from `from_matches` here.
821    let (input, options, render_options, loaded_paths) =
822        match config::Options::from_matches(early_dcx, &matches, args) {
823            Some(opts) => opts,
824            None => return,
825        };
826
827    let dcx =
828        core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts);
829    let dcx = dcx.handle();
830
831    let input = match input {
832        config::InputMode::HasFile(input) => input,
833        config::InputMode::NoInputMergeFinalize => {
834            return wrap_return(
835                dcx,
836                run_merge_finalize(render_options)
837                    .map_err(|e| format!("could not write merged cross-crate info: {e}")),
838            );
839        }
840    };
841
842    let output_format = options.output_format;
843
844    match (
845        options.should_test || output_format == config::OutputFormat::Doctest,
846        config::markdown_input(&input),
847    ) {
848        (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(&input, options)),
849        (true, None) => return doctest::run(dcx, input, options),
850        (false, Some(md_input)) => {
851            let md_input = md_input.to_owned();
852            let edition = options.edition;
853            let config = core::create_config(input, options, &render_options);
854
855            // `markdown::render` can invoke `doctest::make_test`, which
856            // requires session globals and a thread pool, so we use
857            // `run_compiler`.
858            return wrap_return(
859                dcx,
860                interface::run_compiler(config, |_compiler| {
861                    markdown::render_and_write(&md_input, render_options, edition)
862                }),
863            );
864        }
865        (false, None) => {}
866    }
867
868    // need to move these items separately because we lose them by the time the closure is called,
869    // but we can't create the dcx ahead of time because it's not Send
870    let show_coverage = options.show_coverage;
871    let run_check = options.run_check;
872
873    // First, parse the crate and extract all relevant information.
874    info!("starting to run rustc");
875
876    // Interpret the input file as a rust source file, passing it through the
877    // compiler all the way through the analysis passes. The rustdoc output is
878    // then generated from the cleaned AST of the crate. This runs all the
879    // plug/cleaning passes.
880    let crate_version = options.crate_version.clone();
881
882    let scrape_examples_options = options.scrape_examples_options.clone();
883    let bin_crate = options.bin_crate;
884
885    let output_format = options.output_format;
886    let config = core::create_config(input, options, &render_options);
887
888    let registered_lints = config.register_lints.is_some();
889
890    interface::run_compiler(config, |compiler| {
891        let sess = &compiler.sess;
892
893        // Register the loaded external files in the source map so they show up in depinfo.
894        // We can't load them via the source map because it gets created after we process the options.
895        for external_path in &loaded_paths {
896            let _ = sess.source_map().load_file(external_path);
897        }
898
899        if sess.opts.describe_lints {
900            rustc_driver::describe_lints(sess, registered_lints);
901            return;
902        }
903
904        let krate = rustc_interface::passes::parse(sess);
905        rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| {
906            if sess.dcx().has_errors().is_some() {
907                sess.dcx().fatal("Compilation failed, aborting rustdoc");
908            }
909
910            let (krate, render_opts, mut cache, expanded_macros) = sess
911                .time("run_global_ctxt", || {
912                    core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
913                });
914            info!("finished with rustc");
915
916            if let Some(options) = scrape_examples_options {
917                return scrape_examples::run(krate, render_opts, cache, tcx, options, bin_crate);
918            }
919
920            cache.crate_version = crate_version;
921
922            if show_coverage {
923                // if we ran coverage, bail early, we don't need to also generate docs at this point
924                // (also we didn't load in any of the useful passes)
925                return;
926            }
927
928            if render_opts.dep_info().is_some() {
929                rustc_interface::passes::write_dep_info(tcx);
930            }
931
932            if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
933                dump_feature_usage_metrics(tcx, metrics_dir);
934            }
935
936            if run_check {
937                // Since we're in "check" mode, no need to generate anything beyond this point.
938                return;
939            }
940
941            info!("going to format");
942            match output_format {
943                config::OutputFormat::Html => sess.time("render_html", || {
944                    run_renderer(
945                        krate,
946                        render_opts,
947                        cache,
948                        tcx,
949                        |krate, render_opts, cache, tcx| {
950                            html::render::Context::init(
951                                krate,
952                                render_opts,
953                                cache,
954                                tcx,
955                                expanded_macros,
956                            )
957                        },
958                    )
959                }),
960                config::OutputFormat::Json => sess.time("render_json", || {
961                    run_renderer(krate, render_opts, cache, tcx, json::JsonRenderer::init)
962                }),
963                // Already handled above with doctest runners.
964                config::OutputFormat::Doctest => unreachable!(),
965            }
966        })
967    })
968}
969
970fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) {
971    let hash = tcxt.crate_hash(LOCAL_CRATE);
972    let crate_name = tcxt.crate_name(LOCAL_CRATE);
973    let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json");
974    let metrics_path = metrics_dir.join(metrics_file_name);
975    if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) {
976        // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
977        // default metrics" to only produce a warning when metrics are enabled by default and emit
978        // an error only when the user manually enables metrics
979        tcxt.dcx().err(format!("cannot emit feature usage metrics: {error}"));
980    }
981}