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(formatting_options)]
13#![feature(if_let_guard)]
14#![feature(iter_advance_by)]
15#![feature(iter_intersperse)]
16#![feature(iter_order_by)]
17#![feature(rustc_private)]
18#![feature(test)]
19#![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_expand;
38extern crate rustc_feature;
39extern crate rustc_hir;
40extern crate rustc_hir_analysis;
41extern crate rustc_hir_pretty;
42extern crate rustc_index;
43extern crate rustc_infer;
44extern crate rustc_interface;
45extern crate rustc_lexer;
46extern crate rustc_lint;
47extern crate rustc_lint_defs;
48extern crate rustc_log;
49extern crate rustc_macros;
50extern crate rustc_metadata;
51extern crate rustc_middle;
52extern crate rustc_parse;
53extern crate rustc_passes;
54extern crate rustc_resolve;
55extern crate rustc_serialize;
56extern crate rustc_session;
57extern crate rustc_span;
58extern crate rustc_target;
59extern crate rustc_trait_selection;
60extern crate test;
61
62// See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
63// about jemalloc.
64#[cfg(feature = "jemalloc")]
65extern crate tikv_jemalloc_sys as jemalloc_sys;
66
67use std::env::{self, VarError};
68use std::io::{self, IsTerminal};
69use std::path::Path;
70use std::process;
71
72use rustc_errors::DiagCtxtHandle;
73use rustc_hir::def_id::LOCAL_CRATE;
74use rustc_interface::interface;
75use rustc_middle::ty::TyCtxt;
76use rustc_session::config::{ErrorOutputType, RustcOptGroup, make_crate_type_option};
77use rustc_session::{EarlyDiagCtxt, getopts};
78use tracing::info;
79
80use crate::clean::utils::DOC_RUST_LANG_ORG_VERSION;
81use crate::error::Error;
82use crate::formats::cache::Cache;
83
84/// A macro to create a FxHashMap.
85///
86/// Example:
87///
88/// ```ignore(cannot-test-this-because-non-exported-macro)
89/// let letters = map!{"a" => "b", "c" => "d"};
90/// ```
91///
92/// Trailing commas are allowed.
93/// Commas between elements are required (even if the expression is a block).
94macro_rules! map {
95    ($( $key: expr => $val: expr ),* $(,)*) => {{
96        let mut map = ::rustc_data_structures::fx::FxIndexMap::default();
97        $( map.insert($key, $val); )*
98        map
99    }}
100}
101
102mod clean;
103mod config;
104mod core;
105mod display;
106mod docfs;
107mod doctest;
108mod error;
109mod externalfiles;
110mod fold;
111mod formats;
112// used by the error-index generator, so it needs to be public
113pub mod html;
114mod json;
115pub(crate) mod lint;
116mod markdown;
117mod passes;
118mod scrape_examples;
119mod theme;
120mod visit;
121mod visit_ast;
122mod visit_lib;
123
124pub fn main() {
125    // See docs in https://github.com/rust-lang/rust/blob/master/compiler/rustc/src/main.rs
126    // about jemalloc.
127    #[cfg(feature = "jemalloc")]
128    {
129        use std::os::raw::{c_int, c_void};
130
131        #[used]
132        static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc;
133        #[used]
134        static _F2: unsafe extern "C" fn(*mut *mut c_void, usize, usize) -> c_int =
135            jemalloc_sys::posix_memalign;
136        #[used]
137        static _F3: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::aligned_alloc;
138        #[used]
139        static _F4: unsafe extern "C" fn(usize) -> *mut c_void = jemalloc_sys::malloc;
140        #[used]
141        static _F5: unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void = jemalloc_sys::realloc;
142        #[used]
143        static _F6: unsafe extern "C" fn(*mut c_void) = jemalloc_sys::free;
144
145        #[cfg(target_os = "macos")]
146        {
147            unsafe extern "C" {
148                fn _rjem_je_zone_register();
149            }
150
151            #[used]
152            static _F7: unsafe extern "C" fn() = _rjem_je_zone_register;
153        }
154    }
155
156    let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
157
158    rustc_driver::install_ice_hook(
159        "https://github.com/rust-lang/rust/issues/new\
160    ?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md",
161        |_| (),
162    );
163
164    // When using CI artifacts with `download-rustc`, tracing is unconditionally built
165    // with `--features=static_max_level_info`, which disables almost all rustdoc logging. To avoid
166    // this, compile our own version of `tracing` that logs all levels.
167    // NOTE: this compiles both versions of tracing unconditionally, because
168    // - The compile time hit is not that bad, especially compared to rustdoc's incremental times, and
169    // - Otherwise, there's no warning that logging is being ignored when `download-rustc` is enabled
170
171    crate::init_logging(&early_dcx);
172    match rustc_log::init_logger(rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")) {
173        Ok(()) => {}
174        // With `download-rustc = true` there are definitely 2 distinct tracing crates in the
175        // dependency graph: one in the downloaded sysroot and one built just now as a dependency of
176        // rustdoc. So the sysroot's tracing is definitely not yet initialized here.
177        //
178        // But otherwise, depending on link style, there may or may not be 2 tracing crates in play.
179        // The one we just initialized in `crate::init_logging` above is rustdoc's direct dependency
180        // on tracing. When rustdoc is built by x.py using Cargo, rustc_driver's and rustc_log's
181        // tracing dependency is distinct from this one and also needs to be initialized (using the
182        // same RUSTDOC_LOG environment variable for both). Other build systems may use just a
183        // single tracing crate throughout the rustc and rustdoc build.
184        //
185        // The reason initializing 2 tracings does not show double logging when `download-rustc =
186        // false` and `debug_logging = true` is because all rustc logging goes only to its version
187        // of tracing (the one in the sysroot) and all of rustdoc's logging only goes to its version
188        // (the one in Cargo.toml).
189        Err(rustc_log::Error::AlreadyInit(_)) => {}
190        Err(error) => early_dcx.early_fatal(error.to_string()),
191    }
192
193    let exit_code = rustc_driver::catch_with_exit_code(|| {
194        let at_args = rustc_driver::args::raw_args(&early_dcx);
195        main_args(&mut early_dcx, &at_args);
196    });
197    process::exit(exit_code);
198}
199
200fn init_logging(early_dcx: &EarlyDiagCtxt) {
201    let color_logs = match env::var("RUSTDOC_LOG_COLOR").as_deref() {
202        Ok("always") => true,
203        Ok("never") => false,
204        Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(),
205        Ok(value) => early_dcx.early_fatal(format!(
206            "invalid log color value '{value}': expected one of always, never, or auto",
207        )),
208        Err(VarError::NotUnicode(value)) => early_dcx.early_fatal(format!(
209            "invalid log color value '{}': expected one of always, never, or auto",
210            value.to_string_lossy()
211        )),
212    };
213    let filter = tracing_subscriber::EnvFilter::from_env("RUSTDOC_LOG");
214    let layer = tracing_tree::HierarchicalLayer::default()
215        .with_writer(io::stderr)
216        .with_ansi(color_logs)
217        .with_targets(true)
218        .with_wraparound(10)
219        .with_verbose_exit(true)
220        .with_verbose_entry(true)
221        .with_indent_amount(2);
222    #[cfg(debug_assertions)]
223    let layer = layer.with_thread_ids(true).with_thread_names(true);
224
225    use tracing_subscriber::layer::SubscriberExt;
226    let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
227    tracing::subscriber::set_global_default(subscriber).unwrap();
228}
229
230fn opts() -> Vec<RustcOptGroup> {
231    use rustc_session::config::OptionKind::{Flag, FlagMulti, Multi, Opt};
232    use rustc_session::config::OptionStability::{Stable, Unstable};
233    use rustc_session::config::make_opt as opt;
234
235    vec![
236        opt(Stable, FlagMulti, "h", "help", "show this help message", ""),
237        opt(Stable, FlagMulti, "V", "version", "print rustdoc's version", ""),
238        opt(Stable, FlagMulti, "v", "verbose", "use verbose output", ""),
239        opt(Stable, Opt, "w", "output-format", "the output type to write", "[html]"),
240        opt(
241            Stable,
242            Opt,
243            "",
244            "output",
245            "Which directory to place the output. This option is deprecated, use --out-dir instead.",
246            "PATH",
247        ),
248        opt(Stable, Opt, "o", "out-dir", "which directory to place the output", "PATH"),
249        opt(Stable, Opt, "", "crate-name", "specify the name of this crate", "NAME"),
250        make_crate_type_option(),
251        opt(Stable, Multi, "L", "library-path", "directory to add to crate search path", "DIR"),
252        opt(Stable, Multi, "", "cfg", "pass a --cfg to rustc", ""),
253        opt(Stable, Multi, "", "check-cfg", "pass a --check-cfg to rustc", ""),
254        opt(Stable, Multi, "", "extern", "pass an --extern to rustc", "NAME[=PATH]"),
255        opt(
256            Unstable,
257            Multi,
258            "",
259            "extern-html-root-url",
260            "base URL to use for dependencies; for example, \
261                \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html",
262            "NAME=URL",
263        ),
264        opt(
265            Unstable,
266            FlagMulti,
267            "",
268            "extern-html-root-takes-precedence",
269            "give precedence to `--extern-html-root-url`, not `html_root_url`",
270            "",
271        ),
272        opt(Stable, Multi, "C", "codegen", "pass a codegen option to rustc", "OPT[=VALUE]"),
273        opt(Stable, FlagMulti, "", "document-private-items", "document private items", ""),
274        opt(
275            Unstable,
276            FlagMulti,
277            "",
278            "document-hidden-items",
279            "document items that have doc(hidden)",
280            "",
281        ),
282        opt(Stable, FlagMulti, "", "test", "run code examples as tests", ""),
283        opt(Stable, Multi, "", "test-args", "arguments to pass to the test runner", "ARGS"),
284        opt(
285            Stable,
286            Opt,
287            "",
288            "test-run-directory",
289            "The working directory in which to run tests",
290            "PATH",
291        ),
292        opt(Stable, Opt, "", "target", "target triple to document", "TRIPLE"),
293        opt(
294            Stable,
295            Multi,
296            "",
297            "markdown-css",
298            "CSS files to include via <link> in a rendered Markdown file",
299            "FILES",
300        ),
301        opt(
302            Stable,
303            Multi,
304            "",
305            "html-in-header",
306            "files to include inline in the <head> section of a rendered Markdown file \
307                or generated documentation",
308            "FILES",
309        ),
310        opt(
311            Stable,
312            Multi,
313            "",
314            "html-before-content",
315            "files to include inline between <body> and the content of a rendered \
316                Markdown file or generated documentation",
317            "FILES",
318        ),
319        opt(
320            Stable,
321            Multi,
322            "",
323            "html-after-content",
324            "files to include inline between the content and </body> of a rendered \
325                Markdown file or generated documentation",
326            "FILES",
327        ),
328        opt(
329            Unstable,
330            Multi,
331            "",
332            "markdown-before-content",
333            "files to include inline between <body> and the content of a rendered \
334                Markdown file or generated documentation",
335            "FILES",
336        ),
337        opt(
338            Unstable,
339            Multi,
340            "",
341            "markdown-after-content",
342            "files to include inline between the content and </body> of a rendered \
343                Markdown file or generated documentation",
344            "FILES",
345        ),
346        opt(Stable, Opt, "", "markdown-playground-url", "URL to send code snippets to", "URL"),
347        opt(Stable, FlagMulti, "", "markdown-no-toc", "don't include table of contents", ""),
348        opt(
349            Stable,
350            Opt,
351            "e",
352            "extend-css",
353            "To add some CSS rules with a given file to generate doc with your own theme. \
354                However, your theme might break if the rustdoc's generated HTML changes, so be careful!",
355            "PATH",
356        ),
357        opt(
358            Unstable,
359            Multi,
360            "Z",
361            "",
362            "unstable / perma-unstable options (only on nightly build)",
363            "FLAG",
364        ),
365        opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"),
366        opt(
367            Unstable,
368            Opt,
369            "",
370            "playground-url",
371            "URL to send code snippets to, may be reset by --markdown-playground-url \
372                or `#![doc(html_playground_url=...)]`",
373            "URL",
374        ),
375        opt(
376            Unstable,
377            FlagMulti,
378            "",
379            "display-doctest-warnings",
380            "show warnings that originate in doctests",
381            "",
382        ),
383        opt(
384            Stable,
385            Opt,
386            "",
387            "crate-version",
388            "crate version to print into documentation",
389            "VERSION",
390        ),
391        opt(
392            Unstable,
393            FlagMulti,
394            "",
395            "sort-modules-by-appearance",
396            "sort modules by where they appear in the program, rather than alphabetically",
397            "",
398        ),
399        opt(
400            Stable,
401            Opt,
402            "",
403            "default-theme",
404            "Set the default theme. THEME should be the theme name, generally lowercase. \
405                If an unknown default theme is specified, the builtin default is used. \
406                The set of themes, and the rustdoc built-in default, are not stable.",
407            "THEME",
408        ),
409        opt(
410            Unstable,
411            Multi,
412            "",
413            "default-setting",
414            "Default value for a rustdoc setting (used when \"rustdoc-SETTING\" is absent \
415                from web browser Local Storage). If VALUE is not supplied, \"true\" is used. \
416                Supported SETTINGs and VALUEs are not documented and not stable.",
417            "SETTING[=VALUE]",
418        ),
419        opt(
420            Stable,
421            Multi,
422            "",
423            "theme",
424            "additional themes which will be added to the generated docs",
425            "FILES",
426        ),
427        opt(Stable, Multi, "", "check-theme", "check if given theme is valid", "FILES"),
428        opt(
429            Unstable,
430            Opt,
431            "",
432            "resource-suffix",
433            "suffix to add to CSS and JavaScript files, \
434                e.g., \"search-index.js\" will become \"search-index-suffix.js\"",
435            "PATH",
436        ),
437        opt(
438            Stable,
439            Opt,
440            "",
441            "edition",
442            "edition to use when compiling rust code (default: 2015)",
443            "EDITION",
444        ),
445        opt(
446            Stable,
447            Opt,
448            "",
449            "color",
450            "Configure coloring of output:
451                                          auto   = colorize, if output goes to a tty (default);
452                                          always = always colorize output;
453                                          never  = never colorize output",
454            "auto|always|never",
455        ),
456        opt(
457            Stable,
458            Opt,
459            "",
460            "error-format",
461            "How errors and other messages are produced",
462            "human|json|short",
463        ),
464        opt(
465            Stable,
466            Opt,
467            "",
468            "diagnostic-width",
469            "Provide width of the output for truncated error messages",
470            "WIDTH",
471        ),
472        opt(Stable, Opt, "", "json", "Configure the structure of JSON diagnostics", "CONFIG"),
473        opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"),
474        opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"),
475        opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"),
476        opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"),
477        opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"),
478        opt(
479            Stable,
480            Multi,
481            "",
482            "cap-lints",
483            "Set the most restrictive lint level. \
484                More restrictive lints are capped at this level. \
485                By default, it is at `forbid` level.",
486            "LEVEL",
487        ),
488        opt(Unstable, Opt, "", "index-page", "Markdown file to be used as index page", "PATH"),
489        opt(
490            Unstable,
491            FlagMulti,
492            "",
493            "enable-index-page",
494            "To enable generation of the index page",
495            "",
496        ),
497        opt(
498            Unstable,
499            Opt,
500            "",
501            "static-root-path",
502            "Path string to force loading static files from in output pages. \
503                If not set, uses combinations of '../' to reach the documentation root.",
504            "PATH",
505        ),
506        opt(
507            Unstable,
508            Opt,
509            "",
510            "persist-doctests",
511            "Directory to persist doctest executables into",
512            "PATH",
513        ),
514        opt(
515            Unstable,
516            FlagMulti,
517            "",
518            "show-coverage",
519            "calculate percentage of public items with documentation",
520            "",
521        ),
522        opt(
523            Stable,
524            Opt,
525            "",
526            "test-runtool",
527            "",
528            "The tool to run tests with when building for a different target than host",
529        ),
530        opt(
531            Stable,
532            Multi,
533            "",
534            "test-runtool-arg",
535            "",
536            "One argument (of possibly many) to pass to the runtool",
537        ),
538        opt(
539            Unstable,
540            Opt,
541            "",
542            "test-builder",
543            "The rustc-like binary to use as the test builder",
544            "PATH",
545        ),
546        opt(
547            Unstable,
548            Multi,
549            "",
550            "test-builder-wrapper",
551            "Wrapper program to pass test-builder and arguments",
552            "PATH",
553        ),
554        opt(Unstable, FlagMulti, "", "check", "Run rustdoc checks", ""),
555        opt(
556            Unstable,
557            FlagMulti,
558            "",
559            "generate-redirect-map",
560            "Generate JSON file at the top level instead of generating HTML redirection files",
561            "",
562        ),
563        opt(
564            Unstable,
565            Multi,
566            "",
567            "emit",
568            "Comma separated list of types of output for rustdoc to emit",
569            "[unversioned-shared-resources,toolchain-shared-resources,invocation-specific,dep-info]",
570        ),
571        opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""),
572        opt(
573            Unstable,
574            Multi,
575            "",
576            "remap-path-prefix",
577            "Remap source names in compiler messages",
578            "FROM=TO",
579        ),
580        opt(
581            Unstable,
582            FlagMulti,
583            "",
584            "show-type-layout",
585            "Include the memory layout of types in the docs",
586            "",
587        ),
588        opt(Unstable, Flag, "", "nocapture", "Don't capture stdout and stderr of tests", ""),
589        opt(
590            Unstable,
591            Flag,
592            "",
593            "generate-link-to-definition",
594            "Make the identifiers in the HTML source code pages navigable",
595            "",
596        ),
597        opt(
598            Unstable,
599            Opt,
600            "",
601            "scrape-examples-output-path",
602            "",
603            "collect function call information and output at the given path",
604        ),
605        opt(
606            Unstable,
607            Multi,
608            "",
609            "scrape-examples-target-crate",
610            "",
611            "collect function call information for functions from the target crate",
612        ),
613        opt(Unstable, Flag, "", "scrape-tests", "Include test code when scraping examples", ""),
614        opt(
615            Unstable,
616            Multi,
617            "",
618            "with-examples",
619            "",
620            "path to function call information (for displaying examples in the documentation)",
621        ),
622        opt(
623            Unstable,
624            Opt,
625            "",
626            "merge",
627            "Controls how rustdoc handles files from previously documented crates in the doc root\n\
628                none = Do not write cross-crate information to the --out-dir\n\
629                shared = Append current crate's info to files found in the --out-dir\n\
630                finalize = Write current crate's info and --include-parts-dir info to the --out-dir, overwriting conflicting files",
631            "none|shared|finalize",
632        ),
633        opt(
634            Unstable,
635            Opt,
636            "",
637            "parts-out-dir",
638            "Writes trait implementations and other info for the current crate to provided path. Only use with --merge=none",
639            "path/to/doc.parts/<crate-name>",
640        ),
641        opt(
642            Unstable,
643            Multi,
644            "",
645            "include-parts-dir",
646            "Includes trait implementations and other crate info from provided path. Only use with --merge=finalize",
647            "path/to/doc.parts/<crate-name>",
648        ),
649        opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""),
650        opt(
651            Unstable,
652            Multi,
653            "",
654            "doctest-build-arg",
655            "One argument (of possibly many) to be used when compiling doctests",
656            "ARG",
657        ),
658        opt(
659            Unstable,
660            FlagMulti,
661            "",
662            "disable-minification",
663            "disable the minification of CSS/JS files (perma-unstable, do not use with cached files)",
664            "",
665        ),
666        opt(
667            Unstable,
668            Flag,
669            "",
670            "generate-macro-expansion",
671            "Add possibility to expand macros in the HTML source code pages",
672            "",
673        ),
674        // deprecated / removed options
675        opt(
676            Stable,
677            Multi,
678            "",
679            "plugin-path",
680            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
681            "DIR",
682        ),
683        opt(
684            Stable,
685            Multi,
686            "",
687            "passes",
688            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
689            "PASSES",
690        ),
691        opt(
692            Stable,
693            Multi,
694            "",
695            "plugins",
696            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
697            "PLUGINS",
698        ),
699        opt(
700            Stable,
701            FlagMulti,
702            "",
703            "no-defaults",
704            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
705            "",
706        ),
707        opt(
708            Stable,
709            Opt,
710            "r",
711            "input-format",
712            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
713            "[rust]",
714        ),
715    ]
716}
717
718fn usage(argv0: &str) {
719    let mut options = getopts::Options::new();
720    for option in opts() {
721        option.apply(&mut options);
722    }
723    println!("{}", options.usage(&format!("{argv0} [options] <input>")));
724    println!("    @path               Read newline separated options from `path`\n");
725    println!(
726        "More information available at {DOC_RUST_LANG_ORG_VERSION}/rustdoc/what-is-rustdoc.html",
727    );
728}
729
730pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) {
731    match res {
732        Ok(()) => dcx.abort_if_errors(),
733        Err(err) => dcx.fatal(err),
734    }
735}
736
737fn run_renderer<
738    'tcx,
739    T: formats::FormatRenderer<'tcx>,
740    F: FnOnce(
741        clean::Crate,
742        config::RenderOptions,
743        Cache,
744        TyCtxt<'tcx>,
745    ) -> Result<(T, clean::Crate), Error>,
746>(
747    krate: clean::Crate,
748    renderopts: config::RenderOptions,
749    cache: formats::cache::Cache,
750    tcx: TyCtxt<'tcx>,
751    init: F,
752) {
753    match formats::run_format::<T, F>(krate, renderopts, cache, tcx, init) {
754        Ok(_) => tcx.dcx().abort_if_errors(),
755        Err(e) => {
756            let mut msg =
757                tcx.dcx().struct_fatal(format!("couldn't generate documentation: {}", e.error));
758            let file = e.file.display().to_string();
759            if !file.is_empty() {
760                msg.note(format!("failed to create or modify {e}"));
761            } else {
762                msg.note(format!("failed to create or modify file: {e}"));
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                rustc_span::create_session_globals_then(options.edition, &[], None, || {
837                    run_merge_finalize(render_options)
838                        .map_err(|e| format!("could not write merged cross-crate info: {e}"))
839                }),
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}