From 576c9d178188272d4322c236a2d29a2800207638 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 5 Aug 2026 17:36:43 +0200 Subject: [PATCH 1/2] Create output file after we checked that the markdown file is valid --- src/librustdoc/markdown.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index 594c9b1af3397..3cdd4a7e707c5 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -69,13 +69,14 @@ pub(crate) fn render_and_write( let playground_url = options.markdown_playground_url.or(options.playground_url); let playground = playground_url.map(|url| markdown::Playground { crate_name: None, url }); - let mut out = - File::create(&output).map_err(|e| format!("{output}: {e}", output = output.display()))?; - let (metadata, text) = extract_leading_metadata(&input_str); if metadata.is_empty() { return Err("invalid markdown file: no initial lines starting with `# ` or `%`".to_owned()); } + + let mut out = + File::create(&output).map_err(|e| format!("{output}: {e}", output = output.display()))?; + let title = metadata[0]; let error_codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); From 1de2daabc3d378054d3581b8ab7148b4252467a3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 5 Aug 2026 17:37:05 +0200 Subject: [PATCH 2/2] Add regression test for rustdoc invalid standalone markdown --- .../rustdoc/markdown-without-title/rmake.rs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/run-make/rustdoc/markdown-without-title/rmake.rs diff --git a/tests/run-make/rustdoc/markdown-without-title/rmake.rs b/tests/run-make/rustdoc/markdown-without-title/rmake.rs new file mode 100644 index 0000000000000..5bac98c348110 --- /dev/null +++ b/tests/run-make/rustdoc/markdown-without-title/rmake.rs @@ -0,0 +1,45 @@ +// When rustdoc gets a markdown file as input, we want to ensure that if the markdown is invalid, +// the output file won't be truncated in case this markdown is invalid. + +//@ needs-target-std + +use run_make_support::{path, rfs, rustdoc}; + +fn main() { + let output_content = "output"; + let base_file_name = "input"; + + let out_dir = path("out"); + rfs::create_dir(&out_dir); + + // We create the file that should be created by rustdoc and add some content + // into it that we will check is still there once rustdoc failed. + let output = out_dir.join(format!("{base_file_name}.html")); + rfs::write(&output, output_content); + + // We create an "invalid" markdown file (ie no title). + let md_file = format!("{base_file_name}.md"); + rfs::write(&md_file, "Markdown without a title"); + + // We run the failing rustdoc. + rustdoc() + .input(&md_file) + .out_dir(&out_dir) + .run_fail() + .assert_exit_code(1) + .assert_stderr_contains( + "error: invalid markdown file: no initial lines starting with `# ` or `%`", + ); + + // Shouldn't have changed. + assert_eq!(rfs::read_to_string(&output), output_content); + + // We update the input markdown to make it valid for rustdoc. + rfs::write(&md_file, "# a title\n\nMarkdown with a title"); + + // We run rustdoc successfully. + rustdoc().input(&md_file).out_dir(&out_dir).run(); + + // Should have changed. + assert_ne!(rfs::read_to_string(output), output_content); +}