rustdoc/
lib.rs

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