diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index c7bd7321f0c5c..8adb83b29e89a 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -451,6 +451,16 @@ impl FileName { _ => None, } } + + /// Returns the path suitable for reading from the file system on the local host, + /// if this information exists. + pub fn remapped_path_if_available(&self) -> Option<&Path> { + match self { + FileName::Real(path) => Some(path.remapped_path_if_available()), + FileName::DocTest(path, _) => Some(path), + _ => return None, + } + } } /// Represents a span. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 73eafaf78ca58..746eaf61dec29 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -5,7 +5,7 @@ mod rust; use std::fs::File; use std::io::{self, Write}; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; use std::process::{self, Command, Stdio}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; @@ -713,6 +713,22 @@ impl IndividualTestOptions { } } +fn beautify_path(path: &Path) -> PathBuf { + let mut beautiful_path = PathBuf::new(); + + for component in path.components().filter(|c| !matches!(c, Component::CurDir)) { + if matches!(component, Component::ParentDir) { + if beautiful_path.parent().is_none() { + return path.canonicalize().unwrap_or_else(|_| path.to_path_buf()); + } + beautiful_path.pop(); + } else { + beautiful_path.push(component.as_os_str()); + } + } + beautiful_path +} + /// A doctest scraped from the code, ready to be turned into a runnable test. /// /// The pipeline goes: [`clean`] AST -> `ScrapedDoctest` -> `RunnableDoctest`. @@ -743,8 +759,12 @@ impl ScrapedDocTest { if !item_path.is_empty() { item_path.push(' '); } - let name = - format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly()); + let name = match filename.remapped_path_if_available() { + Some(path) => format!("{} - {item_path}(line {line})", beautify_path(path).display()), + None => { + format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionaly()) + } + }; Self { filename, line, langstr, text, name } } diff --git a/tests/run-make/rustdoc-test-path/foo.rs b/tests/run-make/rustdoc-test-path/foo.rs new file mode 100644 index 0000000000000..b6f9b8df4977b --- /dev/null +++ b/tests/run-make/rustdoc-test-path/foo.rs @@ -0,0 +1,2 @@ +#![doc = include_str!("./sub/bar.md")] +#![doc = include_str!("sub/../sub/bar.md")] diff --git a/tests/run-make/rustdoc-test-path/rmake.rs b/tests/run-make/rustdoc-test-path/rmake.rs new file mode 100644 index 0000000000000..43f7fdb49d7b3 --- /dev/null +++ b/tests/run-make/rustdoc-test-path/rmake.rs @@ -0,0 +1,14 @@ +// This test ensures that the file paths displayed in doctests are "beautified", +// meaning they don't contain ".." or "." characters. + +use run_make_support::rustdoc; + +fn main() { + rustdoc() + .input("foo.rs") + .arg("--test") + .run_fail() + .assert_stdout_not_contains("-- ../") + .assert_stdout_not_contains("-- ./") + .assert_stdout_contains("-- sub/bar.md "); +} diff --git a/tests/run-make/rustdoc-test-path/sub/bar.md b/tests/run-make/rustdoc-test-path/sub/bar.md new file mode 100644 index 0000000000000..a8e61238f3115 --- /dev/null +++ b/tests/run-make/rustdoc-test-path/sub/bar.md @@ -0,0 +1,7 @@ +With a code sample, that has an error: + +```rust +fn main() { + let x = 234 // no semicolon here! oh no! +} +```