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