Skip to content

Commit f827364

Browse files
committed
Auto merge of rust-lang#129337 - EtomicBomb:rfc, r=notriddle
rustdoc rfc#3662 changes under unstable flags * All new functionality is under unstable options * Adds `--merge=shared|none|finalize` flags * Adds `--parts-out-dir=<crate specific directory>` for `--merge=none` to write cross-crate info file for a single crate * Adds `--include-parts-dir=<previously specified directory>` for `--merge=finalize` to write cross-crate info files * `tests/rustdoc/` tests for the new flags
2 parents 26b2b8d + 548b6e1 commit f827364

File tree

51 files changed

+953
-169
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+953
-169
lines changed

Diff for: src/librustdoc/config.rs

+110-15
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,18 @@ impl TryFrom<&str> for OutputFormat {
5353
}
5454
}
5555

56+
/// Either an input crate, markdown file, or nothing (--merge=finalize).
57+
pub(crate) enum InputMode {
58+
/// The `--merge=finalize` step does not need an input crate to rustdoc.
59+
NoInputMergeFinalize,
60+
/// A crate or markdown file.
61+
HasFile(Input),
62+
}
63+
5664
/// Configuration options for rustdoc.
5765
#[derive(Clone)]
5866
pub(crate) struct Options {
5967
// Basic options / Options passed directly to rustc
60-
/// The crate root or Markdown file to load.
61-
pub(crate) input: Input,
6268
/// The name of the crate being documented.
6369
pub(crate) crate_name: Option<String>,
6470
/// Whether or not this is a bin crate
@@ -179,7 +185,6 @@ impl fmt::Debug for Options {
179185
}
180186

181187
f.debug_struct("Options")
182-
.field("input", &self.input.source_name())
183188
.field("crate_name", &self.crate_name)
184189
.field("bin_crate", &self.bin_crate)
185190
.field("proc_macro_crate", &self.proc_macro_crate)
@@ -289,6 +294,12 @@ pub(crate) struct RenderOptions {
289294
/// This field is only used for the JSON output. If it's set to true, no file will be created
290295
/// and content will be displayed in stdout directly.
291296
pub(crate) output_to_stdout: bool,
297+
/// Whether we should read or write rendered cross-crate info in the doc root.
298+
pub(crate) should_merge: ShouldMerge,
299+
/// Path to crate-info for external crates.
300+
pub(crate) include_parts_dir: Vec<PathToParts>,
301+
/// Where to write crate-info
302+
pub(crate) parts_out_dir: Option<PathToParts>,
292303
}
293304

294305
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -348,7 +359,7 @@ impl Options {
348359
early_dcx: &mut EarlyDiagCtxt,
349360
matches: &getopts::Matches,
350361
args: Vec<String>,
351-
) -> Option<(Options, RenderOptions)> {
362+
) -> Option<(InputMode, Options, RenderOptions)> {
352363
// Check for unstable options.
353364
nightly_options::check_nightly_options(early_dcx, matches, &opts());
354365

@@ -478,22 +489,34 @@ impl Options {
478489
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
479490

480491
let input = if describe_lints {
481-
"" // dummy, this won't be used
492+
InputMode::HasFile(make_input(early_dcx, ""))
482493
} else {
483494
match matches.free.as_slice() {
495+
[] if matches.opt_str("merge").as_deref() == Some("finalize") => {
496+
InputMode::NoInputMergeFinalize
497+
}
484498
[] => dcx.fatal("missing file operand"),
485-
[input] => input,
499+
[input] => InputMode::HasFile(make_input(early_dcx, input)),
486500
_ => dcx.fatal("too many file operands"),
487501
}
488502
};
489-
let input = make_input(early_dcx, input);
490503

491504
let externs = parse_externs(early_dcx, matches, &unstable_opts);
492505
let extern_html_root_urls = match parse_extern_html_roots(matches) {
493506
Ok(ex) => ex,
494507
Err(err) => dcx.fatal(err),
495508
};
496509

510+
let parts_out_dir =
511+
match matches.opt_str("parts-out-dir").map(|p| PathToParts::from_flag(p)).transpose() {
512+
Ok(parts_out_dir) => parts_out_dir,
513+
Err(e) => dcx.fatal(e),
514+
};
515+
let include_parts_dir = match parse_include_parts_dir(matches) {
516+
Ok(include_parts_dir) => include_parts_dir,
517+
Err(e) => dcx.fatal(e),
518+
};
519+
497520
let default_settings: Vec<Vec<(String, String)>> = vec![
498521
matches
499522
.opt_str("default-theme")
@@ -735,6 +758,10 @@ impl Options {
735758
let extern_html_root_takes_precedence =
736759
matches.opt_present("extern-html-root-takes-precedence");
737760
let html_no_source = matches.opt_present("html-no-source");
761+
let should_merge = match parse_merge(matches) {
762+
Ok(result) => result,
763+
Err(e) => dcx.fatal(format!("--merge option error: {e}")),
764+
};
738765

739766
if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) {
740767
dcx.struct_warn(
@@ -751,7 +778,6 @@ impl Options {
751778
let unstable_features =
752779
rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
753780
let options = Options {
754-
input,
755781
bin_crate,
756782
proc_macro_crate,
757783
error_format,
@@ -823,16 +849,17 @@ impl Options {
823849
no_emit_shared: false,
824850
html_no_source,
825851
output_to_stdout,
852+
should_merge,
853+
include_parts_dir,
854+
parts_out_dir,
826855
};
827-
Some((options, render_options))
856+
Some((input, options, render_options))
828857
}
858+
}
829859

830-
/// Returns `true` if the file given as `self.input` is a Markdown file.
831-
pub(crate) fn markdown_input(&self) -> Option<&Path> {
832-
self.input
833-
.opt_path()
834-
.filter(|p| matches!(p.extension(), Some(e) if e == "md" || e == "markdown"))
835-
}
860+
/// Returns `true` if the file given as `self.input` is a Markdown file.
861+
pub(crate) fn markdown_input(input: &Input) -> Option<&Path> {
862+
input.opt_path().filter(|p| matches!(p.extension(), Some(e) if e == "md" || e == "markdown"))
836863
}
837864

838865
fn parse_remap_path_prefix(
@@ -900,3 +927,71 @@ fn parse_extern_html_roots(
900927
}
901928
Ok(externs)
902929
}
930+
931+
/// Path directly to crate-info file.
932+
///
933+
/// For example, `/home/user/project/target/doc.parts/<crate>/crate-info`.
934+
#[derive(Clone, Debug)]
935+
pub(crate) struct PathToParts(pub(crate) PathBuf);
936+
937+
impl PathToParts {
938+
fn from_flag(path: String) -> Result<PathToParts, String> {
939+
let mut path = PathBuf::from(path);
940+
// check here is for diagnostics
941+
if path.exists() && !path.is_dir() {
942+
Err(format!(
943+
"--parts-out-dir and --include-parts-dir expect directories, found: {}",
944+
path.display(),
945+
))
946+
} else {
947+
// if it doesn't exist, we'll create it. worry about that in write_shared
948+
path.push("crate-info");
949+
Ok(PathToParts(path))
950+
}
951+
}
952+
}
953+
954+
/// Reports error if --include-parts-dir / crate-info is not a file
955+
fn parse_include_parts_dir(m: &getopts::Matches) -> Result<Vec<PathToParts>, String> {
956+
let mut ret = Vec::new();
957+
for p in m.opt_strs("include-parts-dir") {
958+
let p = PathToParts::from_flag(p)?;
959+
// this is just for diagnostic
960+
if !p.0.is_file() {
961+
return Err(format!("--include-parts-dir expected {} to be a file", p.0.display()));
962+
}
963+
ret.push(p);
964+
}
965+
Ok(ret)
966+
}
967+
968+
/// Controls merging of cross-crate information
969+
#[derive(Debug, Clone)]
970+
pub(crate) struct ShouldMerge {
971+
/// Should we append to existing cci in the doc root
972+
pub(crate) read_rendered_cci: bool,
973+
/// Should we write cci to the doc root
974+
pub(crate) write_rendered_cci: bool,
975+
}
976+
977+
/// Extracts read_rendered_cci and write_rendered_cci from command line arguments, or
978+
/// reports an error if an invalid option was provided
979+
fn parse_merge(m: &getopts::Matches) -> Result<ShouldMerge, &'static str> {
980+
match m.opt_str("merge").as_deref() {
981+
// default = read-write
982+
None => Ok(ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }),
983+
Some("none") if m.opt_present("include-parts-dir") => {
984+
Err("--include-parts-dir not allowed if --merge=none")
985+
}
986+
Some("none") => Ok(ShouldMerge { read_rendered_cci: false, write_rendered_cci: false }),
987+
Some("shared") if m.opt_present("parts-out-dir") || m.opt_present("include-parts-dir") => {
988+
Err("--parts-out-dir and --include-parts-dir not allowed if --merge=shared")
989+
}
990+
Some("shared") => Ok(ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }),
991+
Some("finalize") if m.opt_present("parts-out-dir") => {
992+
Err("--parts-out-dir not allowed if --merge=finalize")
993+
}
994+
Some("finalize") => Ok(ShouldMerge { read_rendered_cci: false, write_rendered_cci: true }),
995+
Some(_) => Err("argument to --merge must be `none`, `shared`, or `finalize`"),
996+
}
997+
}

Diff for: src/librustdoc/core.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_interface::interface;
2020
use rustc_lint::{late_lint_mod, MissingDoc};
2121
use rustc_middle::hir::nested_filter;
2222
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
23-
use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
23+
use rustc_session::config::{self, CrateType, ErrorOutputType, Input, ResolveDocLinks};
2424
pub(crate) use rustc_session::config::{Options, UnstableOptions};
2525
use rustc_session::{lint, Session};
2626
use rustc_span::symbol::sym;
@@ -177,8 +177,8 @@ pub(crate) fn new_dcx(
177177

178178
/// Parse, resolve, and typecheck the given crate.
179179
pub(crate) fn create_config(
180+
input: Input,
180181
RustdocOptions {
181-
input,
182182
crate_name,
183183
proc_macro_crate,
184184
error_format,

Diff for: src/librustdoc/doctest.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_errors::{ColorConfig, DiagCtxtHandle, ErrorGuaranteed, FatalError};
1919
use rustc_hir::def_id::LOCAL_CRATE;
2020
use rustc_hir::CRATE_HIR_ID;
2121
use rustc_interface::interface;
22-
use rustc_session::config::{self, CrateType, ErrorOutputType};
22+
use rustc_session::config::{self, CrateType, ErrorOutputType, Input};
2323
use rustc_session::lint;
2424
use rustc_span::edition::Edition;
2525
use rustc_span::symbol::sym;
@@ -88,7 +88,11 @@ fn get_doctest_dir() -> io::Result<TempDir> {
8888
TempFileBuilder::new().prefix("rustdoctest").tempdir()
8989
}
9090

91-
pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
91+
pub(crate) fn run(
92+
dcx: DiagCtxtHandle<'_>,
93+
input: Input,
94+
options: RustdocOptions,
95+
) -> Result<(), ErrorGuaranteed> {
9296
let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name;
9397

9498
// See core::create_config for what's going on here.
@@ -135,7 +139,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<()
135139
opts: sessopts,
136140
crate_cfg: cfgs,
137141
crate_check_cfg: options.check_cfgs.clone(),
138-
input: options.input.clone(),
142+
input: input.clone(),
139143
output_file: None,
140144
output_dir: None,
141145
file_loader: None,

Diff for: src/librustdoc/doctest/markdown.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::fs::read_to_string;
44
use std::sync::{Arc, Mutex};
55

6+
use rustc_session::config::Input;
67
use rustc_span::FileName;
78
use tempfile::tempdir;
89

@@ -69,17 +70,16 @@ impl DocTestVisitor for MdCollector {
6970
}
7071

7172
/// Runs any tests/code examples in the markdown file `options.input`.
72-
pub(crate) fn test(options: Options) -> Result<(), String> {
73-
use rustc_session::config::Input;
74-
let input_str = match &options.input {
73+
pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> {
74+
let input_str = match input {
7575
Input::File(path) => {
7676
read_to_string(path).map_err(|err| format!("{}: {err}", path.display()))?
7777
}
7878
Input::Str { name: _, input } => input.clone(),
7979
};
8080

8181
// Obviously not a real crate name, but close enough for purposes of doctests.
82-
let crate_name = options.input.filestem().to_string();
82+
let crate_name = input.filestem().to_string();
8383
let temp_dir =
8484
tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?;
8585
let args_file = temp_dir.path().join("rustdoc-cfgs");
@@ -96,8 +96,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
9696
let mut md_collector = MdCollector {
9797
tests: vec![],
9898
cur_path: vec![],
99-
filename: options
100-
.input
99+
filename: input
101100
.opt_path()
102101
.map(ToOwned::to_owned)
103102
.map(FileName::from)

0 commit comments

Comments
 (0)