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