Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 41 additions & 12 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ use crate::html::static_files;
use crate::scrape_examples::{AllCallLocations, ScrapeExamplesOptions};
use crate::{html, opts, theme};

#[cfg(test)]
mod tests;

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum OutputFormat {
/// `--output-format=json` without `--show-coverage`.
Expand Down Expand Up @@ -75,8 +78,6 @@ pub(crate) struct Options {
pub(crate) diagnostic_width: Option<usize>,
/// Library search paths to hand to the compiler.
pub(crate) libs: Vec<SearchPath>,
/// Library search paths strings to hand to the compiler.
pub(crate) lib_strs: Vec<String>,
/// The list of external crates to link against.
pub(crate) externs: Externs,
/// The list of external crates strings to link against.
Expand All @@ -87,12 +88,12 @@ pub(crate) struct Options {
pub(crate) check_cfgs: Vec<String>,
/// Codegen options to hand to the compiler.
pub(crate) codegen_options: CodegenOptions,
/// Codegen options strings to hand to the compiler.
pub(crate) codegen_options_strs: Vec<String>,
/// Unstable (`-Z`) options to pass to the compiler.
pub(crate) unstable_opts: UnstableOptions,
/// Unstable (`-Z`) options strings to pass to the compiler.
pub(crate) unstable_opts_strs: Vec<String>,
/// Compiler options, in command-line order, to hand to the compiler building doctests.
pub(crate) forwarded_rustc_args: Vec<String>,
/// Native libraries (`-l`) to link into doctest binaries.
pub(crate) native_lib_strs: Vec<String>,
/// The target used to compile the crate against.
pub(crate) target: TargetTuple,
/// Edition used when reading the crate. Defaults to "2015". Also used by default when
Expand Down Expand Up @@ -853,9 +854,8 @@ impl Options {
let test_run_directory = matches.opt_str("test-run-directory").map(PathBuf::from);
let persist_doctests = matches.opt_str("persist-doctests").map(PathBuf::from);
let test_builder = matches.opt_str("test-builder").map(PathBuf::from);
let codegen_options_strs = matches.opt_strs("C");
let unstable_opts_strs = matches.opt_strs("Z");
let lib_strs = matches.opt_strs("L");
let forwarded_rustc_args = forwarded_rustc_args(matches);
let native_lib_strs = matches.opt_strs("l");
let extern_strs = matches.opt_strs("extern");
let test_runtool = matches.opt_str("test-runtool");
let test_runtool_args = matches.opt_strs("test-runtool-arg");
Expand Down Expand Up @@ -902,15 +902,14 @@ impl Options {
error_format,
diagnostic_width,
libs,
lib_strs,
externs,
extern_strs,
cfgs,
check_cfgs,
codegen_options,
codegen_options_strs,
unstable_opts,
unstable_opts_strs,
forwarded_rustc_args,
native_lib_strs,
target,
edition,
sysroot,
Expand Down Expand Up @@ -986,6 +985,36 @@ pub(crate) fn markdown_input(input: &Input) -> Option<&Path> {
input.opt_path().filter(|p| matches!(p.extension(), Some(e) if e == "md" || e == "markdown"))
}

/// Re-serializes, in their original command-line order, the options that the compiler building
/// doctests must see. Order is preserved because rustc resolves `-O` against `-C opt-level` and
/// `-g` against `-C debuginfo` by position.
///
/// `-l` is deliberately absent: these arguments are shared with the merged-doctest runner, which
/// links the native libraries through the bundle rlib and must not be given them a second time.
pub(crate) fn forwarded_rustc_args(matches: &getopts::Matches) -> Vec<String> {
let mut args = Vec::new();
for (name, prefix) in [
("cfg", "--cfg="),
("check-cfg", "--check-cfg="),
("library-path", "-L"),
("extern", "--extern="),
("codegen", "-C"),
("Z", "-Z"),
] {
args.extend(
matches
.opt_strs_pos(name)
.into_iter()
.map(|(pos, val)| (pos, format!("{prefix}{val}"))),
);
}
for name in ["O", "g"] {
args.extend(matches.opt_positions(name).into_iter().map(|pos| (pos, format!("-{name}"))));
}
args.sort_by_key(|(pos, _)| *pos);
args.into_iter().map(|(_, arg)| arg).collect()
}

fn parse_remap_path_prefix(
matches: &getopts::Matches,
) -> Result<Vec<(PathBuf, PathBuf)>, &'static str> {
Expand Down
104 changes: 104 additions & 0 deletions src/librustdoc/config/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use rustc_session::config::rustc_optgroups;
use rustc_session::getopts;

use crate::opts;

fn rustdoc_getopts() -> getopts::Options {
let mut options = getopts::Options::new();
for option in opts() {
option.apply(&mut options);
}
options
}

#[test]
fn every_rustc_option_is_accepted_by_rustdoc() {
let options = rustdoc_getopts();
let unrecognized: Vec<String> = rustc_optgroups()
.iter()
.map(|opt| {
let long = opt.long_name();
if long.is_empty() { format!("-{}", opt.name) } else { format!("--{long}") }
})
.filter(|arg| {
matches!(
options.parse([arg.as_str(), "dummy"]),
Err(getopts::Fail::UnrecognizedOption(_))
)
})
.collect();
assert!(unrecognized.is_empty(), "rustc options rejected by rustdoc: {unrecognized:?}");
}

#[test]
fn every_override_names_a_rustc_option() {
let rustc_names: Vec<&str> = rustc_optgroups().iter().map(|opt| opt.name).collect();
let stale: Vec<&str> =
crate::RUSTDOC_OVERRIDES.iter().copied().filter(|n| !rustc_names.contains(n)).collect();
assert!(stale.is_empty(), "overrides that no longer match a rustc option: {stale:?}");
}

#[test]
fn options_are_declared_once() {
let mut names: Vec<&str> = opts().iter().map(|opt| opt.name).collect();
names.sort_unstable();
let before = names.len();
names.dedup();
assert_eq!(before, names.len(), "duplicate option declarations: {names:?}");
}

#[test]
fn forwarded_rustc_args_preserve_command_line_order() {
let matches = rustdoc_getopts()
.parse([
"-Copt-level=1",
"--cfg",
"a",
"-O",
"-Zfoo",
"-lbar",
"-Lx",
"--library-path=y",
"--extern",
"e=path",
"-g",
"--codegen",
"debuginfo=1",
"--check-cfg",
"cfg(a)",
"--out-dir",
"ignored",
"input.rs",
])
.unwrap();
assert_eq!(
super::forwarded_rustc_args(&matches),
[
"-Copt-level=1",
"--cfg=a",
"-O",
"-Zfoo",
"-Lx",
"-Ly",
"--extern=e=path",
"-g",
"-Cdebuginfo=1",
"--check-cfg=cfg(a)",
]
);
}

#[test]
fn overridden_short_flags_resolve_to_rustdoc_declarations() {
let matches = rustdoc_getopts()
.parse([
"-o", "out", "-L", "search", "-h", "-h", "-V", "-V", "-v", "-v", "--test", "--test",
])
.unwrap();
assert_eq!(matches.opt_str("out-dir").as_deref(), Some("out"));
assert_eq!(matches.opt_strs("library-path"), ["search"]);
assert_eq!(matches.opt_count("help"), 2);
assert_eq!(matches.opt_count("version"), 2);
assert_eq!(matches.opt_count("verbose"), 2);
assert_eq!(matches.opt_count("test"), 2);
}
24 changes: 4 additions & 20 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,8 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) ->
// We now put the common arguments into the file we created.
let mut content = vec![];

for cfg in &options.cfgs {
content.push(format!("--cfg={cfg}"));
}
for check_cfg in &options.check_cfgs {
content.push(format!("--check-cfg={check_cfg}"));
}

for lib_str in &options.lib_strs {
content.push(format!("-L{lib_str}"));
}
for extern_str in &options.extern_strs {
content.push(format!("--extern={extern_str}"));
}
content.push("-Ccodegen-units=1".to_string());
for codegen_options_str in &options.codegen_options_strs {
content.push(format!("-C{codegen_options_str}"));
}
for unstable_option_str in &options.unstable_opts_strs {
content.push(format!("-Z{unstable_option_str}"));
}

content.extend(options.forwarded_rustc_args.iter().cloned());
content.extend(options.doctest_build_args.clone());

let content = content.join("\n");
Expand Down Expand Up @@ -695,6 +676,9 @@ fn run_test(
let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary);

compiler.args(&compiler_args);
for lib in &rustdoc_options.native_lib_strs {
compiler.arg(format!("-l{lib}"));
}

// If this is a merged doctest, we need to write it into a file instead of using stdin
// because if the size of the merged doctests is too big, it'll simply break stdin.
Expand Down
Loading
Loading