Skip to content

Ensure external paths passed via flags end up in rustdoc depinfo #144600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 29, 2025
Merged
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
20 changes: 13 additions & 7 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ impl Options {
early_dcx: &mut EarlyDiagCtxt,
matches: &getopts::Matches,
args: Vec<String>,
) -> Option<(InputMode, Options, RenderOptions)> {
) -> Option<(InputMode, Options, RenderOptions, Vec<PathBuf>)> {
// Check for unstable options.
nightly_options::check_nightly_options(early_dcx, matches, &opts());

Expand Down Expand Up @@ -640,10 +640,13 @@ impl Options {

let extension_css = matches.opt_str("e").map(|s| PathBuf::from(&s));

if let Some(ref p) = extension_css
&& !p.is_file()
{
dcx.fatal("option --extend-css argument must be a file");
let mut loaded_paths = Vec::new();

if let Some(ref p) = extension_css {
loaded_paths.push(p.clone());
if !p.is_file() {
dcx.fatal("option --extend-css argument must be a file");
}
}

let mut themes = Vec::new();
Expand Down Expand Up @@ -687,6 +690,7 @@ impl Options {
))
.emit();
}
loaded_paths.push(theme_file.clone());
themes.push(StylePath { path: theme_file });
}
}
Expand All @@ -705,6 +709,7 @@ impl Options {
&mut id_map,
edition,
&None,
&mut loaded_paths,
) else {
dcx.fatal("`ExternalHtml::load` failed");
};
Expand Down Expand Up @@ -796,7 +801,8 @@ impl Options {

let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx);
let with_examples = matches.opt_strs("with-examples");
let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx);
let call_locations =
crate::scrape_examples::load_call_locations(with_examples, dcx, &mut loaded_paths);
let doctest_build_args = matches.opt_strs("doctest-build-arg");

let unstable_features =
Expand Down Expand Up @@ -881,7 +887,7 @@ impl Options {
parts_out_dir,
disable_minification,
};
Some((input, options, render_options))
Some((input, options, render_options, loaded_paths))
}
}

Expand Down
23 changes: 15 additions & 8 deletions src/librustdoc/externalfiles.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use std::{fs, str};

use rustc_errors::DiagCtxtHandle;
Expand Down Expand Up @@ -32,12 +32,13 @@ impl ExternalHtml {
id_map: &mut IdMap,
edition: Edition,
playground: &Option<Playground>,
loaded_paths: &mut Vec<PathBuf>,
) -> Option<ExternalHtml> {
let codes = ErrorCodes::from(nightly_build);
let ih = load_external_files(in_header, dcx)?;
let ih = load_external_files(in_header, dcx, loaded_paths)?;
let bc = {
let mut bc = load_external_files(before_content, dcx)?;
let m_bc = load_external_files(md_before_content, dcx)?;
let mut bc = load_external_files(before_content, dcx, loaded_paths)?;
let m_bc = load_external_files(md_before_content, dcx, loaded_paths)?;
Markdown {
content: &m_bc,
links: &[],
Expand All @@ -52,8 +53,8 @@ impl ExternalHtml {
bc
};
let ac = {
let mut ac = load_external_files(after_content, dcx)?;
let m_ac = load_external_files(md_after_content, dcx)?;
let mut ac = load_external_files(after_content, dcx, loaded_paths)?;
let m_ac = load_external_files(md_after_content, dcx, loaded_paths)?;
Markdown {
content: &m_ac,
links: &[],
Expand All @@ -79,8 +80,10 @@ pub(crate) enum LoadStringError {
pub(crate) fn load_string<P: AsRef<Path>>(
file_path: P,
dcx: DiagCtxtHandle<'_>,
loaded_paths: &mut Vec<PathBuf>,
) -> Result<String, LoadStringError> {
let file_path = file_path.as_ref();
loaded_paths.push(file_path.to_owned());
let contents = match fs::read(file_path) {
Ok(bytes) => bytes,
Err(e) => {
Expand All @@ -101,10 +104,14 @@ pub(crate) fn load_string<P: AsRef<Path>>(
}
}

fn load_external_files(names: &[String], dcx: DiagCtxtHandle<'_>) -> Option<String> {
fn load_external_files(
names: &[String],
dcx: DiagCtxtHandle<'_>,
loaded_paths: &mut Vec<PathBuf>,
) -> Option<String> {
let mut out = String::new();
for name in names {
let Ok(s) = load_string(name, dcx) else { return None };
let Ok(s) = load_string(name, dcx, loaded_paths) else { return None };
out.push_str(&s);
out.push('\n');
}
Expand Down
8 changes: 7 additions & 1 deletion src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {

// Note that we discard any distinction between different non-zero exit
// codes from `from_matches` here.
let (input, options, render_options) =
let (input, options, render_options, loaded_paths) =
match config::Options::from_matches(early_dcx, &matches, args) {
Some(opts) => opts,
None => return,
Expand Down Expand Up @@ -870,6 +870,12 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
interface::run_compiler(config, |compiler| {
let sess = &compiler.sess;

// Register the loaded external files in the source map so they show up in depinfo.
// We can't load them via the source map because it gets created after we process the options.
for external_path in &loaded_paths {
let _ = sess.source_map().load_file(external_path);
}

if sess.opts.describe_lints {
rustc_driver::describe_lints(sess, registered_lints);
return;
Expand Down
2 changes: 2 additions & 0 deletions src/librustdoc/scrape_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,11 @@ pub(crate) fn run(
pub(crate) fn load_call_locations(
with_examples: Vec<String>,
dcx: DiagCtxtHandle<'_>,
loaded_paths: &mut Vec<PathBuf>,
) -> AllCallLocations {
let mut all_calls: AllCallLocations = FxIndexMap::default();
for path in with_examples {
loaded_paths.push(path.clone().into());
let bytes = match fs::read(&path) {
Ok(bytes) => bytes,
Err(e) => dcx.fatal(format!("failed to load examples: {e}")),
Expand Down
1 change: 1 addition & 0 deletions tests/run-make/rustdoc-dep-info/after.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
meow! :3
Empty file.
Empty file.
15 changes: 14 additions & 1 deletion tests/run-make/rustdoc-dep-info/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,26 @@ use run_make_support::{path, rfs, rustdoc};
fn main() {
// We're only emitting dep info, so we shouldn't be running static analysis to
// figure out that this program is erroneous.
rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info").run();
// Ensure that all kinds of input reading flags end up in dep-info.
rustdoc()
.input("lib.rs")
.arg("-Zunstable-options")
.arg("--html-before-content=before.html")
.arg("--markdown-after-content=after.md")
.arg("--extend-css=extend.css")
.arg("--theme=theme.css")
.emit("dep-info")
.run();

let content = rfs::read_to_string("foo.d");
assert_contains(&content, "lib.rs:");
assert_contains(&content, "foo.rs:");
assert_contains(&content, "bar.rs:");
assert_contains(&content, "doc.md:");
assert_contains(&content, "after.md:");
assert_contains(&content, "before.html:");
assert_contains(&content, "extend.css:");
assert_contains(&content, "theme.css:");

// Now we check that we can provide a file name to the `dep-info` argument.
rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info=bla.d").run();
Expand Down
1 change: 1 addition & 0 deletions tests/run-make/rustdoc-dep-info/theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* This is not a valid theme but that doesn't really matter */
Loading