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