[go: up one dir, main page]

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;
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        // deprecated / removed options
667        opt(
668            Stable,
669            Multi,
670            "",
671            "plugin-path",
672            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
673            "DIR",
674        ),
675        opt(
676            Stable,
677            Multi,
678            "",
679            "passes",
680            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
681            "PASSES",
682        ),
683        opt(
684            Stable,
685            Multi,
686            "",
687            "plugins",
688            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
689            "PLUGINS",
690        ),
691        opt(
692            Stable,
693            FlagMulti,
694            "",
695            "no-defaults",
696            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
697            "",
698        ),
699        opt(
700            Stable,
701            Opt,
702            "r",
703            "input-format",
704            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
705            "[rust]",
706        ),
707    ]
708}
709
710fn usage(argv0: &str) {
711    let mut options = getopts::Options::new();
712    for option in opts() {
713        option.apply(&mut options);
714    }
715    println!("{}", options.usage(&format!("{argv0} [options] <input>")));
716    println!("    @path               Read newline separated options from `path`\n");
717    println!(
718        "More information available at {DOC_RUST_LANG_ORG_VERSION}/rustdoc/what-is-rustdoc.html",
719    );
720}
721
722pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) {
723    match res {
724        Ok(()) => dcx.abort_if_errors(),
725        Err(err) => dcx.fatal(err),
726    }
727}
728
729fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
730    krate: clean::Crate,
731    renderopts: config::RenderOptions,
732    cache: formats::cache::Cache,
733    tcx: TyCtxt<'tcx>,
734) {
735    match formats::run_format::<T>(krate, renderopts, cache, tcx) {
736        Ok(_) => tcx.dcx().abort_if_errors(),
737        Err(e) => {
738            let mut msg =
739                tcx.dcx().struct_fatal(format!("couldn't generate documentation: {}", e.error));
740            let file = e.file.display().to_string();
741            if !file.is_empty() {
742                msg.note(format!("failed to create or modify \"{file}\""));
743            }
744            msg.emit();
745        }
746    }
747}
748
749/// Renders and writes cross-crate info files, like the search index. This function exists so that
750/// we can run rustdoc without a crate root in the `--merge=finalize` mode. Cross-crate info files
751/// discovered via `--include-parts-dir` are combined and written to the doc root.
752fn run_merge_finalize(opt: config::RenderOptions) -> Result<(), error::Error> {
753    assert!(
754        opt.should_merge.write_rendered_cci,
755        "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize"
756    );
757    assert!(
758        !opt.should_merge.read_rendered_cci,
759        "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize"
760    );
761    let crates = html::render::CrateInfo::read_many(&opt.include_parts_dir)?;
762    let include_sources = !opt.html_no_source;
763    html::render::write_not_crate_specific(
764        &crates,
765        &opt.output,
766        &opt,
767        &opt.themes,
768        opt.extension_css.as_deref(),
769        &opt.resource_suffix,
770        include_sources,
771    )?;
772    Ok(())
773}
774
775fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
776    // Throw away the first argument, the name of the binary.
777    // In case of at_args being empty, as might be the case by
778    // passing empty argument array to execve under some platforms,
779    // just use an empty slice.
780    //
781    // This situation was possible before due to arg_expand_all being
782    // called before removing the argument, enabling a crash by calling
783    // the compiler with @empty_file as argv[0] and no more arguments.
784    let at_args = at_args.get(1..).unwrap_or_default();
785
786    let args = rustc_driver::args::arg_expand_all(early_dcx, at_args);
787
788    let mut options = getopts::Options::new();
789    for option in opts() {
790        option.apply(&mut options);
791    }
792    let matches = match options.parse(&args) {
793        Ok(m) => m,
794        Err(err) => {
795            early_dcx.early_fatal(err.to_string());
796        }
797    };
798
799    // Note that we discard any distinction between different non-zero exit
800    // codes from `from_matches` here.
801    let (input, options, render_options, loaded_paths) =
802        match config::Options::from_matches(early_dcx, &matches, args) {
803            Some(opts) => opts,
804            None => return,
805        };
806
807    let dcx =
808        core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts);
809    let dcx = dcx.handle();
810
811    let input = match input {
812        config::InputMode::HasFile(input) => input,
813        config::InputMode::NoInputMergeFinalize => {
814            return wrap_return(
815                dcx,
816                run_merge_finalize(render_options)
817                    .map_err(|e| format!("could not write merged cross-crate info: {e}")),
818            );
819        }
820    };
821
822    let output_format = options.output_format;
823
824    match (
825        options.should_test || output_format == config::OutputFormat::Doctest,
826        config::markdown_input(&input),
827    ) {
828        (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(&input, options)),
829        (true, None) => return doctest::run(dcx, input, options),
830        (false, Some(md_input)) => {
831            let md_input = md_input.to_owned();
832            let edition = options.edition;
833            let config = core::create_config(input, options, &render_options);
834
835            // `markdown::render` can invoke `doctest::make_test`, which
836            // requires session globals and a thread pool, so we use
837            // `run_compiler`.
838            return wrap_return(
839                dcx,
840                interface::run_compiler(config, |_compiler| {
841                    markdown::render_and_write(&md_input, render_options, edition)
842                }),
843            );
844        }
845        (false, None) => {}
846    }
847
848    // need to move these items separately because we lose them by the time the closure is called,
849    // but we can't create the dcx ahead of time because it's not Send
850    let show_coverage = options.show_coverage;
851    let run_check = options.run_check;
852
853    // First, parse the crate and extract all relevant information.
854    info!("starting to run rustc");
855
856    // Interpret the input file as a rust source file, passing it through the
857    // compiler all the way through the analysis passes. The rustdoc output is
858    // then generated from the cleaned AST of the crate. This runs all the
859    // plug/cleaning passes.
860    let crate_version = options.crate_version.clone();
861
862    let scrape_examples_options = options.scrape_examples_options.clone();
863    let bin_crate = options.bin_crate;
864
865    let config = core::create_config(input, options, &render_options);
866
867    let registered_lints = config.register_lints.is_some();
868
869    interface::run_compiler(config, |compiler| {
870        let sess = &compiler.sess;
871
872        // Register the loaded external files in the source map so they show up in depinfo.
873        // We can't load them via the source map because it gets created after we process the options.
874        for external_path in &loaded_paths {
875            let _ = sess.source_map().load_file(external_path);
876        }
877
878        if sess.opts.describe_lints {
879            rustc_driver::describe_lints(sess, registered_lints);
880            return;
881        }
882
883        let krate = rustc_interface::passes::parse(sess);
884        rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| {
885            if sess.dcx().has_errors().is_some() {
886                sess.dcx().fatal("Compilation failed, aborting rustdoc");
887            }
888
889            let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
890                core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
891            });
892            info!("finished with rustc");
893
894            if let Some(options) = scrape_examples_options {
895                return scrape_examples::run(krate, render_opts, cache, tcx, options, bin_crate);
896            }
897
898            cache.crate_version = crate_version;
899
900            if show_coverage {
901                // if we ran coverage, bail early, we don't need to also generate docs at this point
902                // (also we didn't load in any of the useful passes)
903                return;
904            }
905
906            if render_opts.dep_info().is_some() {
907                rustc_interface::passes::write_dep_info(tcx);
908            }
909
910            if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
911                dump_feature_usage_metrics(tcx, metrics_dir);
912            }
913
914            if run_check {
915                // Since we're in "check" mode, no need to generate anything beyond this point.
916                return;
917            }
918
919            info!("going to format");
920            match output_format {
921                config::OutputFormat::Html => sess.time("render_html", || {
922                    run_renderer::<html::render::Context<'_>>(krate, render_opts, cache, tcx)
923                }),
924                config::OutputFormat::Json => sess.time("render_json", || {
925                    run_renderer::<json::JsonRenderer<'_>>(krate, render_opts, cache, tcx)
926                }),
927                // Already handled above with doctest runners.
928                config::OutputFormat::Doctest => unreachable!(),
929            }
930        })
931    })
932}
933
934fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) {
935    let hash = tcxt.crate_hash(LOCAL_CRATE);
936    let crate_name = tcxt.crate_name(LOCAL_CRATE);
937    let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json");
938    let metrics_path = metrics_dir.join(metrics_file_name);
939    if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) {
940        // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
941        // default metrics" to only produce a warning when metrics are enabled by default and emit
942        // an error only when the user manually enables metrics
943        tcxt.dcx().err(format!("cannot emit feature usage metrics: {error}"));
944    }
945}