Skip to main content

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(default_field_values)]
9#![feature(deref_patterns)]
10#![feature(exitcode_exit_method)]
11#![feature(file_buffered)]
12#![feature(formatting_options)]
13#![feature(iter_intersperse)]
14#![feature(iter_order_by)]
15#![feature(iter_partition_in_place)]
16#![feature(rustc_private)]
17#![feature(test)]
18#![feature(variant_count)]
19#![recursion_limit = "256"]
20#![warn(rustc::internal)]
21#![warn(rustc::symbol_intern_string_literal)]
22// tidy-alphabetical-end
23
24// N.B. these need `extern crate` even in 2018 edition
25// because they're loaded implicitly from the sysroot.
26// The reason they're loaded from the sysroot is because
27// the rustdoc artifacts aren't stored in rustc's cargo target directory.
28// So if `rustc` was specified in Cargo.toml, this would spuriously rebuild crates.
29//
30// Dependencies listed in Cargo.toml do not need `extern crate`.
31
32extern crate rustc_abi;
33extern crate rustc_ast;
34extern crate rustc_ast_pretty;
35extern crate rustc_attr_ir;
36extern crate rustc_attr_parsing;
37extern crate rustc_data_structures;
38extern crate rustc_driver;
39extern crate rustc_errors;
40extern crate rustc_feature;
41extern crate rustc_hir;
42extern crate rustc_hir_analysis;
43extern crate rustc_hir_pretty;
44extern crate rustc_index;
45extern crate rustc_infer;
46extern crate rustc_interface;
47extern crate rustc_lexer;
48extern crate rustc_lint;
49extern crate rustc_log;
50extern crate rustc_macros;
51extern crate rustc_metadata;
52extern crate rustc_middle;
53extern crate rustc_parse;
54extern crate rustc_passes;
55extern crate rustc_resolve;
56extern crate rustc_serialize;
57extern crate rustc_session;
58extern crate rustc_span;
59extern crate rustc_structures;
60extern crate rustc_target;
61extern crate rustc_trait_selection;
62extern crate test;
63
64use std::env::{self, VarError};
65use std::io::{self, IsTerminal};
66use std::path::Path;
67use std::process::ExitCode;
68
69use rustc_ast::ast;
70use rustc_errors::DiagCtxtHandle;
71use rustc_hir::def_id::LOCAL_CRATE;
72use rustc_interface::interface;
73use rustc_middle::ty::TyCtxt;
74use rustc_session::config::{ErrorOutputType, Input, RustcOptGroup, make_crate_type_option};
75use rustc_session::{EarlyDiagCtxt, getopts};
76use rustc_span::{BytePos, Span, SyntaxContext};
77use tracing::info;
78
79use crate::clean::utils::DOC_RUST_LANG_ORG_VERSION;
80use crate::config::EmitType;
81use crate::error::Error;
82use crate::formats::cache::Cache;
83
84/// A macro to create a FxHashMap.
85///
86/// Example:
87///
88/// ```ignore(cannot-test-this-because-non-exported-macro)
89/// let letters = map!{"a" => "b", "c" => "d"};
90/// ```
91///
92/// Trailing commas are allowed.
93/// Commas between elements are required (even if the expression is a block).
94macro_rules! map {
95    ($( $key: expr => $val: expr ),* $(,)*) => {{
96        let mut map = ::rustc_data_structures::fx::FxIndexMap::default();
97        $( map.insert($key, $val); )*
98        map
99    }}
100}
101
102mod calculate_doc_coverage;
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() -> ExitCode {
126    let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
127
128    rustc_driver::install_ice_hook(
129        "https://github.com/rust-lang/rust/issues/new\
130    ?labels=C-bug%2C+I-ICE%2C+T-rustdoc&template=ice.md",
131        |_| (),
132    );
133
134    // When using CI artifacts with `download-rustc`, tracing is unconditionally built
135    // with `--features=static_max_level_info`, which disables almost all rustdoc logging. To avoid
136    // this, compile our own version of `tracing` that logs all levels.
137    // NOTE: this compiles both versions of tracing unconditionally, because
138    // - The compile time hit is not that bad, especially compared to rustdoc's incremental times, and
139    // - Otherwise, there's no warning that logging is being ignored when `download-rustc` is enabled
140
141    crate::init_logging(&early_dcx);
142    match rustc_log::init_logger(rustc_log::LoggerConfig::from_env("RUSTDOC_LOG")) {
143        Ok(()) => {}
144        // With `download-rustc = true` there are definitely 2 distinct tracing crates in the
145        // dependency graph: one in the downloaded sysroot and one built just now as a dependency of
146        // rustdoc. So the sysroot's tracing is definitely not yet initialized here.
147        //
148        // But otherwise, depending on link style, there may or may not be 2 tracing crates in play.
149        // The one we just initialized in `crate::init_logging` above is rustdoc's direct dependency
150        // on tracing. When rustdoc is built by x.py using Cargo, rustc_driver's and rustc_log's
151        // tracing dependency is distinct from this one and also needs to be initialized (using the
152        // same RUSTDOC_LOG environment variable for both). Other build systems may use just a
153        // single tracing crate throughout the rustc and rustdoc build.
154        //
155        // The reason initializing 2 tracings does not show double logging when `download-rustc =
156        // false` and `debug_logging = true` is because all rustc logging goes only to its version
157        // of tracing (the one in the sysroot) and all of rustdoc's logging only goes to its version
158        // (the one in Cargo.toml).
159        Err(rustc_log::Error::AlreadyInit(_)) => {}
160        Err(error) => early_dcx.early_fatal(error.to_string()),
161    }
162
163    rustc_driver::catch_with_exit_code(|| {
164        let at_args = rustc_driver::args::raw_args(&early_dcx);
165        main_args(&mut early_dcx, &at_args);
166    })
167}
168
169fn init_logging(early_dcx: &EarlyDiagCtxt) {
170    let color_logs = match env::var("RUSTDOC_LOG_COLOR").as_deref() {
171        Ok("always") => true,
172        Ok("never") => false,
173        Ok("auto") | Err(VarError::NotPresent) => io::stdout().is_terminal(),
174        Ok(value) => early_dcx.early_fatal(format!(
175            "invalid log color value '{value}': expected one of always, never, or auto",
176        )),
177        Err(VarError::NotUnicode(value)) => early_dcx.early_fatal(format!(
178            "invalid log color value '{}': expected one of always, never, or auto",
179            value.to_string_lossy()
180        )),
181    };
182    let filter = tracing_subscriber::EnvFilter::from_env("RUSTDOC_LOG");
183    let layer = tracing_tree::HierarchicalLayer::default()
184        .with_writer(io::stderr)
185        .with_ansi(color_logs)
186        .with_targets(true)
187        .with_wraparound(10)
188        .with_verbose_exit(true)
189        .with_verbose_entry(true)
190        .with_indent_amount(2);
191    #[cfg(debug_assertions)]
192    let layer = layer.with_thread_ids(true).with_thread_names(true);
193
194    use tracing_subscriber::layer::SubscriberExt;
195    let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
196    tracing::subscriber::set_global_default(subscriber).unwrap();
197}
198
199fn opts() -> Vec<RustcOptGroup> {
200    use rustc_session::config::OptionKind::{Flag, FlagMulti, Multi, Opt};
201    use rustc_session::config::OptionStability::{Stable, Unstable};
202    use rustc_session::config::make_opt as opt;
203
204    vec![
205        opt(Stable, FlagMulti, "h", "help", "show this help message", ""),
206        opt(Stable, FlagMulti, "V", "version", "print rustdoc's version", ""),
207        opt(Stable, FlagMulti, "v", "verbose", "use verbose output", ""),
208        opt(Stable, Opt, "w", "output-format", "the output type to write", "[html]"),
209        opt(
210            Stable,
211            Opt,
212            "",
213            "output",
214            "Which directory to place the output. This option is deprecated, use --out-dir instead.",
215            "PATH",
216        ),
217        opt(Stable, Opt, "o", "out-dir", "which directory to place the output", "PATH"),
218        opt(Stable, Opt, "", "crate-name", "specify the name of this crate", "NAME"),
219        make_crate_type_option(),
220        opt(Stable, Multi, "L", "library-path", "directory to add to crate search path", "DIR"),
221        opt(Stable, Multi, "", "cfg", "pass a --cfg to rustc", ""),
222        opt(Stable, Multi, "", "check-cfg", "pass a --check-cfg to rustc", ""),
223        opt(Stable, Multi, "", "extern", "pass an --extern to rustc", "NAME[=PATH]"),
224        opt(
225            Unstable,
226            Multi,
227            "",
228            "extern-html-root-url",
229            "base URL to use for dependencies; for example, \
230                \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html",
231            "NAME=URL",
232        ),
233        opt(
234            Unstable,
235            FlagMulti,
236            "",
237            "extern-html-root-takes-precedence",
238            "give precedence to `--extern-html-root-url`, not `html_root_url`",
239            "",
240        ),
241        opt(Stable, Multi, "C", "codegen", "pass a codegen option to rustc", "OPT[=VALUE]"),
242        opt(Stable, FlagMulti, "", "document-private-items", "document private items", ""),
243        opt(
244            Unstable,
245            FlagMulti,
246            "",
247            "document-hidden-items",
248            "document items that have doc(hidden)",
249            "",
250        ),
251        opt(Stable, FlagMulti, "", "test", "run code examples as tests", ""),
252        opt(Stable, Multi, "", "test-args", "arguments to pass to the test runner", "ARGS"),
253        opt(
254            Stable,
255            Opt,
256            "",
257            "test-run-directory",
258            "The working directory in which to run tests",
259            "PATH",
260        ),
261        opt(Stable, Opt, "", "target", "target triple to document", "TRIPLE"),
262        opt(
263            Stable,
264            Multi,
265            "",
266            "markdown-css",
267            "CSS files to include via <link> in a rendered Markdown file",
268            "FILES",
269        ),
270        opt(
271            Stable,
272            Multi,
273            "",
274            "html-in-header",
275            "files to include inline in the <head> section of a rendered Markdown file \
276                or generated documentation",
277            "FILES",
278        ),
279        opt(
280            Stable,
281            Multi,
282            "",
283            "html-before-content",
284            "files to include inline between <body> and the content of a rendered \
285                Markdown file or generated documentation",
286            "FILES",
287        ),
288        opt(
289            Stable,
290            Multi,
291            "",
292            "html-after-content",
293            "files to include inline between the content and </body> of a rendered \
294                Markdown file or generated documentation",
295            "FILES",
296        ),
297        opt(
298            Unstable,
299            Multi,
300            "",
301            "markdown-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            Unstable,
308            Multi,
309            "",
310            "markdown-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(Stable, Opt, "", "markdown-playground-url", "URL to send code snippets to", "URL"),
316        opt(Stable, FlagMulti, "", "markdown-no-toc", "don't include table of contents", ""),
317        opt(
318            Stable,
319            Opt,
320            "e",
321            "extend-css",
322            "To add some CSS rules with a given file to generate doc with your own theme. \
323                However, your theme might break if the rustdoc's generated HTML changes, so be careful!",
324            "PATH",
325        ),
326        opt(
327            Unstable,
328            Multi,
329            "Z",
330            "",
331            "unstable / perma-unstable options (only on nightly build)",
332            "FLAG",
333        ),
334        opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"),
335        opt(
336            Unstable,
337            Opt,
338            "",
339            "playground-url",
340            "URL to send code snippets to, may be reset by --markdown-playground-url \
341                or `#![doc(html_playground_url=...)]`",
342            "URL",
343        ),
344        opt(
345            Unstable,
346            FlagMulti,
347            "",
348            "display-doctest-warnings",
349            "show warnings that originate in doctests",
350            "",
351        ),
352        opt(
353            Stable,
354            Opt,
355            "",
356            "crate-version",
357            "crate version to print into documentation",
358            "VERSION",
359        ),
360        opt(
361            Unstable,
362            FlagMulti,
363            "",
364            "sort-modules-by-appearance",
365            "sort modules by where they appear in the program, rather than alphabetically",
366            "",
367        ),
368        opt(
369            Unstable,
370            Opt,
371            "",
372            "crate-list-heading",
373            "heading under which to list this crate in the sidebar crate list",
374            "TEXT",
375        ),
376        opt(
377            Stable,
378            Opt,
379            "",
380            "default-theme",
381            "Set the default theme. THEME should be the theme name, generally lowercase. \
382                If an unknown default theme is specified, the builtin default is used. \
383                The set of themes, and the rustdoc built-in default, are not stable.",
384            "THEME",
385        ),
386        opt(
387            Unstable,
388            Multi,
389            "",
390            "default-setting",
391            "Default value for a rustdoc setting (used when \"rustdoc-SETTING\" is absent \
392                from web browser Local Storage). If VALUE is not supplied, \"true\" is used. \
393                Supported SETTINGs and VALUEs are not documented and not stable.",
394            "SETTING[=VALUE]",
395        ),
396        opt(
397            Stable,
398            Multi,
399            "",
400            "theme",
401            "additional themes which will be added to the generated docs",
402            "FILES",
403        ),
404        opt(Stable, Multi, "", "check-theme", "check if given theme is valid", "FILES"),
405        opt(
406            Unstable,
407            Opt,
408            "",
409            "resource-suffix",
410            "suffix to add to CSS and JavaScript files, \
411                e.g., \"search-index.js\" will become \"search-index-suffix.js\"",
412            "PATH",
413        ),
414        opt(
415            Stable,
416            Opt,
417            "",
418            "edition",
419            "edition to use when compiling rust code (default: 2015)",
420            "EDITION",
421        ),
422        opt(
423            Stable,
424            Opt,
425            "",
426            "color",
427            "Configure coloring of output:
428                                          auto   = colorize, if output goes to a tty (default);
429                                          always = always colorize output;
430                                          never  = never colorize output",
431            "auto|always|never",
432        ),
433        opt(
434            Stable,
435            Opt,
436            "",
437            "error-format",
438            "How errors and other messages are produced",
439            "human|json|short",
440        ),
441        opt(
442            Stable,
443            Opt,
444            "",
445            "diagnostic-width",
446            "Provide width of the output for truncated error messages",
447            "WIDTH",
448        ),
449        opt(Stable, Opt, "", "json", "Configure the structure of JSON diagnostics", "CONFIG"),
450        opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"),
451        opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"),
452        opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"),
453        opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"),
454        opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"),
455        opt(
456            Stable,
457            Multi,
458            "",
459            "cap-lints",
460            "Set the most restrictive lint level. \
461                More restrictive lints are capped at this level. \
462                By default, it is at `forbid` level.",
463            "LEVEL",
464        ),
465        opt(
466            Stable,
467            Multi,
468            "",
469            "remap-path-prefix",
470            "Remap source names in compiler messages",
471            "FROM=TO",
472        ),
473        opt(Unstable, Opt, "", "index-page", "Markdown file to be used as index page", "PATH"),
474        opt(
475            Unstable,
476            FlagMulti,
477            "",
478            "enable-index-page",
479            "To enable generation of the index page",
480            "",
481        ),
482        opt(
483            Unstable,
484            Opt,
485            "",
486            "static-root-path",
487            "Path string to force loading static files from in output pages. \
488                If not set, uses combinations of '../' to reach the documentation root.",
489            "PATH",
490        ),
491        opt(
492            Unstable,
493            Opt,
494            "",
495            "persist-doctests",
496            "Directory to persist doctest executables into",
497            "PATH",
498        ),
499        opt(
500            Unstable,
501            FlagMulti,
502            "",
503            "show-coverage",
504            "calculate percentage of public items with documentation",
505            "",
506        ),
507        opt(
508            Stable,
509            Opt,
510            "",
511            "test-runtool",
512            "",
513            "The tool to run tests with when building for a different target than host",
514        ),
515        opt(
516            Stable,
517            Multi,
518            "",
519            "test-runtool-arg",
520            "",
521            "One argument (of possibly many) to pass to the runtool",
522        ),
523        opt(
524            Unstable,
525            Opt,
526            "",
527            "test-builder",
528            "The rustc-like binary to use as the test builder",
529            "PATH",
530        ),
531        opt(
532            Unstable,
533            Multi,
534            "",
535            "test-builder-wrapper",
536            "Wrapper program to pass test-builder and arguments",
537            "PATH",
538        ),
539        opt(Unstable, FlagMulti, "", "check", "Run rustdoc checks", ""),
540        opt(
541            Unstable,
542            FlagMulti,
543            "",
544            "generate-redirect-map",
545            "Generate JSON file at the top level instead of generating HTML redirection files",
546            "",
547        ),
548        opt(
549            Stable,
550            Multi,
551            "",
552            "emit",
553            "Comma separated list of types of output for rustdoc to emit",
554            "[html-static-files,html-non-static-files,dep-info]",
555        ),
556        opt(
557            Unstable,
558            Multi,
559            "",
560            "print",
561            "Rustdoc information to print on stdout (or to a file)",
562            "<INFO>[=<FILE>]",
563        ),
564        opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""),
565        opt(
566            Unstable,
567            Opt,
568            "",
569            "merge-doctests",
570            "Force all doctests to be compiled as a single binary, instead of one binary per test. If merging fails, rustdoc will emit a hard error.",
571            "yes|no|auto",
572        ),
573        opt(
574            Unstable,
575            Opt,
576            "",
577            "remap-path-scope",
578            "Defines which scopes of paths should be remapped by `--remap-path-prefix`",
579            "[macro,diagnostics,debuginfo,coverage,object,all]",
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, "", "no-capture", "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            "write-doc-meta-dir",
628            "Writes trait implementations and other info for the current crate to provided path",
629            "path/to/doc.meta",
630        ),
631        opt(
632            Unstable,
633            Multi,
634            "",
635            "read-doc-meta-dir",
636            "Includes trait implementations and other crate info from provided path",
637            "path/to/doc.meta",
638        ),
639        opt(
640            Unstable,
641            Opt,
642            "",
643            "parts-out-dir",
644            "Deprecated synonym of write-doc-meta-dir",
645            "path/to/doc.meta",
646        ),
647        opt(
648            Unstable,
649            Multi,
650            "",
651            "include-parts-dir",
652            "Deprecated synonym of read-doc-meta-dir",
653            "path/to/doc.meta",
654        ),
655        opt(
656            Unstable,
657            Opt,
658            "",
659            "merge",
660            "Deprecated option to specify read/write-doc-meta-dir mode",
661            "none, shared, finalize",
662        ),
663        opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""),
664        opt(
665            Unstable,
666            Multi,
667            "",
668            "doctest-build-arg",
669            "One argument (of possibly many) to be used when compiling doctests",
670            "ARG",
671        ),
672        opt(
673            Unstable,
674            FlagMulti,
675            "",
676            "disable-minification",
677            "disable the minification of CSS/JS files (perma-unstable, do not use with cached files)",
678            "",
679        ),
680        opt(
681            Unstable,
682            Flag,
683            "",
684            "generate-macro-expansion",
685            "Add possibility to expand macros in the HTML source code pages",
686            "",
687        ),
688        // deprecated / removed options
689        opt(
690            Stable,
691            Multi,
692            "",
693            "plugin-path",
694            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
695            "DIR",
696        ),
697        opt(
698            Stable,
699            Multi,
700            "",
701            "passes",
702            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
703            "PASSES",
704        ),
705        opt(
706            Stable,
707            Multi,
708            "",
709            "plugins",
710            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
711            "PLUGINS",
712        ),
713        opt(
714            Stable,
715            FlagMulti,
716            "",
717            "no-defaults",
718            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
719            "",
720        ),
721        opt(
722            Stable,
723            Opt,
724            "r",
725            "input-format",
726            "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
727            "[rust]",
728        ),
729    ]
730}
731
732fn usage(argv0: &str) {
733    let mut options = getopts::Options::new();
734    for option in opts() {
735        option.apply(&mut options);
736    }
737    println!("{}", options.usage(&format!("{argv0} [options] <input>")));
738    println!("    @path               Read newline separated options from `path`\n");
739    println!(
740        "More information available at {DOC_RUST_LANG_ORG_VERSION}/rustdoc/what-is-rustdoc.html",
741    );
742}
743
744pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) {
745    match res {
746        Ok(()) => dcx.abort_if_errors(),
747        Err(err) => dcx.fatal(err),
748    }
749}
750
751fn run_renderer<
752    'tcx,
753    T: formats::FormatRenderer<'tcx>,
754    F: FnOnce(
755        clean::Crate,
756        config::RenderOptions,
757        Cache,
758        TyCtxt<'tcx>,
759    ) -> Result<(T, clean::Crate), Error>,
760>(
761    krate: clean::Crate,
762    renderopts: config::RenderOptions,
763    cache: formats::cache::Cache,
764    tcx: TyCtxt<'tcx>,
765    init: F,
766) {
767    match formats::run_format::<T, F>(krate, renderopts, cache, tcx, init) {
768        Ok(_) => tcx.dcx().abort_if_errors(),
769        Err(e) => {
770            let mut msg =
771                tcx.dcx().struct_fatal(format!("couldn't generate documentation: {}", e.error));
772            let file = e.file.display().to_string();
773            if !file.is_empty() {
774                msg.note(format!("failed to create or modify {e}"));
775            } else {
776                msg.note(format!("failed to create or modify file: {e}"));
777            }
778            msg.emit();
779        }
780    }
781}
782
783/// Renders and writes cross-crate info files, like the search index. This function exists so that
784/// we can run rustdoc without a crate root in the `--merge=finalize` mode. Cross-crate info files
785/// discovered via `--read-doc-meta-dir` are combined and written to the doc root.
786fn run_merge_finalize(
787    render_options: config::RenderOptions,
788    compiler: &interface::Compiler,
789) -> Result<(), error::Error> {
790    assert!(
791        render_options.should_merge.write_rendered_cci,
792        "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize"
793    );
794    assert!(
795        !render_options.should_merge.read_rendered_cci,
796        "config.rs only allows us to return InputMode::NoInputMergeFinalize if --merge=finalize"
797    );
798    let crates = html::render::CrateInfo::read_many(&render_options.include_parts_dir)?;
799    let include_sources = !render_options.html_no_source;
800
801    html::render::write_not_crate_specific(
802        &crates,
803        &render_options.output,
804        &render_options,
805        &render_options.themes,
806        render_options.extension_css.as_deref(),
807        &render_options.resource_suffix,
808        include_sources,
809        &crate::html::layout::Layout {
810            logo: String::new(),
811            favicon: String::new(),
812            external_html: render_options.external_html.clone(),
813            default_settings: render_options.default_settings.clone(),
814            krate: String::new(),
815            krate_version: String::new(),
816            css_file_extension: render_options.extension_css.clone(),
817            scrape_examples_extension: false,
818        },
819        &compiler.sess,
820    )?;
821    Ok(())
822}
823
824fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
825    // Throw away the first argument, the name of the binary.
826    // In case of at_args being empty, as might be the case by
827    // passing empty argument array to execve under some platforms,
828    // just use an empty slice.
829    //
830    // This situation was possible before due to arg_expand_all being
831    // called before removing the argument, enabling a crash by calling
832    // the compiler with @empty_file as argv[0] and no more arguments.
833    let at_args = at_args.get(1..).unwrap_or_default();
834
835    let args = rustc_driver::args::arg_expand_all(early_dcx, at_args);
836
837    let mut options = getopts::Options::new();
838    for option in opts() {
839        option.apply(&mut options);
840    }
841    let matches = match options.parse(&args) {
842        Ok(m) => m,
843        Err(err) => {
844            early_dcx.early_fatal(err.to_string());
845        }
846    };
847
848    // Note that we discard any distinction between different non-zero exit
849    // codes from `from_matches` here.
850    let (input, options, render_options, loaded_paths) =
851        match config::Options::from_matches(early_dcx, &matches, args) {
852            Some(opts) => opts,
853            None => return,
854        };
855
856    let dcx =
857        core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts);
858    let dcx = dcx.handle();
859
860    let input = match input {
861        config::InputMode::HasFile(input) => input,
862        config::InputMode::NoInputMergeFinalize => {
863            if !options.prints.is_empty() {
864                dcx.fatal("`--print` is not supported for the `--write-doc-meta-dir` option");
865            }
866
867            let config = core::create_config(
868                Input::Str {
869                    name: rustc_span::FileName::Custom(String::new()),
870                    input: String::new(),
871                },
872                options,
873                &render_options,
874            );
875            return wrap_return(
876                dcx,
877                interface::run_compiler(config, |compiler| {
878                    run_merge_finalize(render_options, compiler)
879                        .map_err(|e| format!("could not write merged cross-crate info: {e}"))
880                }),
881            );
882        }
883    };
884    let md_input = config::markdown_input(&input);
885
886    if options.should_test || options.output_format == config::OutputFormat::Doctest {
887        if !options.prints.is_empty() {
888            dcx.fatal(format!(
889                "`--print` is not yet supported for the `{}` option",
890                if options.should_test { "--test" } else { "--output-format=doctest" }
891            ));
892        }
893
894        return match md_input {
895            Some(_) => wrap_return(dcx, doctest::test_markdown(&input, options, dcx)),
896            None => doctest::run(dcx, input, options),
897        };
898    }
899
900    if let Some(md_input) = md_input {
901        if !options.prints.is_empty() {
902            dcx.fatal("`--print` is not yet supported for standalone Markdown files");
903        }
904
905        return {
906            let md_input = md_input.to_owned();
907            let edition = options.edition;
908            let config = core::create_config(input, options, &render_options);
909            let registered_lints = config.register_lints.is_some();
910
911            // `markdown::render` can invoke `doctest::make_test`, which
912            // requires session globals and a thread pool, so we use
913            // `run_compiler`.
914            wrap_return(
915                dcx,
916                interface::run_compiler(config, |compiler| {
917                    let sess = &compiler.sess;
918
919                    // -W help
920                    if sess.opts.describe_lints {
921                        rustc_driver::describe_lints(sess, registered_lints);
922                        return Ok(());
923                    }
924
925                    // construct a phony "crate" without actually running the parser
926                    // allows us to use other compiler infrastructure like dep-info
927                    let file = sess
928                        .source_map()
929                        .load_file(&md_input)
930                        .map_err(|e| format!("{md_input}: {e}", md_input = md_input.display()))?;
931                    let inner_span = Span::new(
932                        file.start_pos,
933                        BytePos(file.start_pos.0 + file.normalized_source_len.0),
934                        SyntaxContext::root(),
935                        None,
936                    );
937                    let krate = ast::Crate {
938                        attrs: Default::default(),
939                        items: Default::default(),
940                        spans: ast::ModSpans { inner_span, ..Default::default() },
941                        id: ast::DUMMY_NODE_ID,
942                        is_placeholder: false,
943                    };
944                    let (res, _incr_comp_session) =
945                        rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| {
946                            let has_dep_info = render_options.dep_info().is_some();
947                            if render_options.emit.contains(&EmitType::HtmlNonStaticFiles) {
948                                markdown::render_and_write(file, render_options, edition)?;
949                            }
950                            if has_dep_info {
951                                // Register the loaded external files in the source map so they show up in depinfo.
952                                // We can't load them via the source map because it gets created after we process the options.
953                                for external_path in &loaded_paths {
954                                    let _ =
955                                        compiler.sess.source_map().load_binary_file(external_path);
956                                }
957                                rustc_interface::passes::write_dep_info(tcx);
958                            }
959                            Ok(())
960                        });
961                    res
962                }),
963            )
964        };
965    }
966
967    // need to move these items separately because we lose them by the time the closure is called,
968    // but we can't create the dcx ahead of time because it's not Send
969    let show_coverage = options.show_coverage;
970    let run_check = options.run_check;
971
972    // First, parse the crate and extract all relevant information.
973    info!("starting to run rustc");
974
975    // Interpret the input file as a rust source file, passing it through the
976    // compiler all the way through the analysis passes. The rustdoc output is
977    // then generated from the cleaned AST of the crate. This runs all the
978    // plug/cleaning passes.
979    let crate_version = options.crate_version.clone();
980
981    let scrape_examples_options = options.scrape_examples_options.clone();
982    let bin_crate = options.bin_crate;
983
984    let output_format = options.output_format;
985    let config = core::create_config(input, options, &render_options);
986    let registered_lints = config.register_lints.is_some();
987
988    interface::run_compiler(config, |compiler| {
989        let sess = &compiler.sess;
990
991        // Register the loaded external files in the source map so they show up in depinfo.
992        // We can't load them via the source map because it gets created after we process the options.
993        for external_path in &loaded_paths {
994            let _ = sess.source_map().load_binary_file(external_path);
995        }
996
997        // -W help
998        if sess.opts.describe_lints {
999            rustc_driver::describe_lints(sess, registered_lints);
1000            return;
1001        }
1002
1003        // --print
1004        if rustc_driver::print_crate_info(&*compiler.codegen_backend, sess, true)
1005            == rustc_driver::Compilation::Stop
1006        {
1007            return;
1008        }
1009
1010        let krate = rustc_interface::passes::parse(sess);
1011        rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| {
1012            if sess.dcx().has_errors().is_some() {
1013                sess.dcx().fatal("Compilation failed, aborting rustdoc");
1014            }
1015
1016            let (krate, render_opts, mut cache, expanded_macros) = sess
1017                .time("run_global_ctxt", || {
1018                    core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
1019                });
1020            info!("finished with rustc");
1021
1022            if let Some(options) = scrape_examples_options {
1023                return scrape_examples::run(krate, render_opts, cache, tcx, options, bin_crate);
1024            }
1025
1026            if show_coverage {
1027                // if we ran coverage, bail early, we don't need to also generate docs at this point
1028                // (also we didn't load in any of the useful passes)
1029                return;
1030            }
1031
1032            cache.crate_version = crate_version;
1033
1034            if render_opts.dep_info().is_some() {
1035                rustc_interface::passes::write_dep_info(tcx);
1036            }
1037
1038            if let Some(metrics_dir) = &sess.opts.unstable_opts.metrics_dir {
1039                dump_feature_usage_metrics(tcx, metrics_dir);
1040            }
1041
1042            if run_check {
1043                // Since we're in "check" mode, no need to generate anything beyond this point.
1044                return;
1045            }
1046
1047            info!("going to format");
1048            match output_format {
1049                config::OutputFormat::Html => sess.time("render_html", || {
1050                    run_renderer(
1051                        krate,
1052                        render_opts,
1053                        cache,
1054                        tcx,
1055                        |krate, render_opts, cache, tcx| {
1056                            html::render::Context::init(
1057                                krate,
1058                                render_opts,
1059                                cache,
1060                                tcx,
1061                                expanded_macros,
1062                            )
1063                        },
1064                    )
1065                }),
1066                config::OutputFormat::IrJson => sess.time("render_json", || {
1067                    run_renderer(krate, render_opts, cache, tcx, json::JsonRenderer::init)
1068                }),
1069                // Already handled above with doctest runners or coverage early return
1070                config::OutputFormat::Doctest | config::OutputFormat::CoverageJson => {
1071                    unreachable!()
1072                }
1073            }
1074        });
1075    })
1076}
1077
1078fn dump_feature_usage_metrics(tcx: TyCtxt<'_>, metrics_dir: &Path) {
1079    let hash = tcx.crate_hash(LOCAL_CRATE);
1080    let crate_name = tcx.crate_name(LOCAL_CRATE);
1081    let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json");
1082    let metrics_path = metrics_dir.join(metrics_file_name);
1083    if let Err(error) = tcx.features().dump_feature_usage_metrics(metrics_path) {
1084        // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit
1085        // default metrics" to only produce a warning when metrics are enabled by default and emit
1086        // an error only when the user manually enables metrics
1087        tcx.dcx().err(format!("cannot emit feature usage metrics: {error}"));
1088    }
1089}