Skip to main content

rustdoc/
lib.rs

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