From b0cb45e6176ca765210f882411e8271ef3ca8584 Mon Sep 17 00:00:00 2001 From: arferreira Date: Mon, 20 Apr 2026 13:14:29 -0400 Subject: [PATCH] Normalize .. and . in diagnostic file paths --- compiler/rustc_span/src/lib.rs | 39 +++++++++++++------ compiler/rustc_span/src/source_map.rs | 2 +- compiler/rustc_span/src/source_map/tests.rs | 11 ++++++ src/tools/compiletest/src/runtest.rs | 10 +++++ tests/ui/README.md | 4 ++ .../generic_arg_infer/issue-91614.stderr | 4 +- tests/ui/diagnostics/auxiliary/helper.rs | 3 ++ tests/ui/diagnostics/auxiliary/sub/mod.rs | 2 + tests/ui/diagnostics/normalize-path.rs | 9 +++++ tests/ui/diagnostics/normalize-path.stderr | 11 ++++++ tests/ui/imports/ambiguous-2.stderr | 8 ++-- tests/ui/imports/ambiguous-4.stderr | 8 ++-- 12 files changed, 88 insertions(+), 23 deletions(-) create mode 100644 tests/ui/diagnostics/auxiliary/helper.rs create mode 100644 tests/ui/diagnostics/auxiliary/sub/mod.rs create mode 100644 tests/ui/diagnostics/normalize-path.rs create mode 100644 tests/ui/diagnostics/normalize-path.stderr diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 97de708290fb4..3d6d1f243a7e2 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -21,6 +21,7 @@ #![feature(core_io_borrowed_buf)] #![feature(map_try_insert)] #![feature(negative_impls)] +#![feature(normalize_lexically)] #![feature(read_buf)] #![feature(rustc_attrs)] // tidy-alphabetical-end @@ -496,6 +497,15 @@ impl RealFileName { .file_name() .map_or_else(|| "".into(), |f| f.to_string_lossy()), FileNameDisplayPreference::Scope(scope) => self.path(scope).to_string_lossy(), + FileNameDisplayPreference::Diagnostics(scope) => { + let path = self.path(scope); + match path.normalize_lexically() { + Ok(normalized) => { + Cow::Owned(normalized.into_os_string().to_string_lossy().into_owned()) + } + Err(_) => path.to_string_lossy(), + } + } } } } @@ -533,15 +543,23 @@ enum FileNameDisplayPreference { Local, Short, Scope(RemapPathScopeComponents), + Diagnostics(RemapPathScopeComponents), +} + +impl<'a> FileNameDisplay<'a> { + pub fn to_string_lossy(&self) -> Cow<'a, str> { + match self.inner { + FileName::Real(inner) => inner.to_string_lossy(self.display_pref), + _ => Cow::from(self.to_string()), + } + } } impl fmt::Display for FileNameDisplay<'_> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use FileName::*; match *self.inner { - Real(ref name) => { - write!(fmt, "{}", name.to_string_lossy(self.display_pref)) - } + Real(ref name) => write!(fmt, "{}", name.to_string_lossy(self.display_pref)), CfgSpec(_) => write!(fmt, ""), MacroExpansion(_) => write!(fmt, ""), Anon(_) => write!(fmt, ""), @@ -554,15 +572,6 @@ impl fmt::Display for FileNameDisplay<'_> { } } -impl<'a> FileNameDisplay<'a> { - pub fn to_string_lossy(&self) -> Cow<'a, str> { - match self.inner { - FileName::Real(inner) => inner.to_string_lossy(self.display_pref), - _ => Cow::from(self.to_string()), - } - } -} - impl FileName { pub fn is_real(&self) -> bool { use FileName::*; @@ -609,6 +618,12 @@ impl FileName { FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Scope(scope) } } + /// Like `display`, but with `.` and `..` resolved lexically. See #51349. + #[inline] + pub fn display_normalized(&self, scope: RemapPathScopeComponents) -> FileNameDisplay<'_> { + FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Diagnostics(scope) } + } + pub fn macro_expansion_source_code(src: &str) -> FileName { let mut hasher = StableHasher::new(); src.hash(&mut hasher); diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 47c933e245d49..e0b3356678fc5 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -487,7 +487,7 @@ impl SourceMap { } pub fn filename_for_diagnostics<'a>(&self, filename: &'a FileName) -> FileNameDisplay<'a> { - filename.display(RemapPathScopeComponents::DIAGNOSTICS) + filename.display_normalized(RemapPathScopeComponents::DIAGNOSTICS) } pub fn is_multiline(&self, sp: Span) -> bool { diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 4cc243667f224..acc186a007fca 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -797,3 +797,14 @@ fn read_binary_file_handles_lying_stat() { let bin = RealFileLoader.read_binary_file(kernel_max).unwrap(); assert_eq!(&real[..], &bin[..]); } + +#[test] +fn filename_for_diagnostics_resolves_parent_dir() { + let sm = SourceMap::new(FilePathMapping::empty()); + + let with_parent = filename(&sm, "tests/sub/../helper.rs"); + assert_eq!(sm.filename_for_diagnostics(&with_parent).to_string(), path_str("tests/helper.rs")); + + let clean = filename(&sm, "tests/clean.rs"); + assert_eq!(sm.filename_for_diagnostics(&clean).to_string(), path_str("tests/clean.rs")); +} diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 6b5147cea6626..99e6df014b4b5 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2485,12 +2485,22 @@ impl<'test> TestCx<'test> { let parent_dir = self.testpaths.file.parent().unwrap(); normalize_path(parent_dir, "$DIR"); + // After #51349, rustc normalizes `tests/x/y/../aux/foo.rs` to + // `tests/x/aux/foo.rs`. Replace the grandparent with `$DIR/..` so + // stderrs keep the pre-normalization form. + if let Some(grandparent_dir) = parent_dir.parent() { + normalize_path(grandparent_dir, "$DIR/.."); + } + if self.props.remap_src_base { let mut remapped_parent_dir = Utf8PathBuf::from(FAKE_SRC_BASE); if self.testpaths.relative_dir != Utf8Path::new("") { remapped_parent_dir.push(&self.testpaths.relative_dir); } normalize_path(&remapped_parent_dir, "$DIR"); + if let Some(remapped_grandparent) = remapped_parent_dir.parent() { + normalize_path(remapped_grandparent, "$DIR/.."); + } } let base_dir = Utf8Path::new("/rustc/FAKE_PREFIX"); diff --git a/tests/ui/README.md b/tests/ui/README.md index 7c2df5048fc1b..a52464241ffc2 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -440,6 +440,10 @@ Everything to do with `--diagnostic-width`. Exercises `#[diagnostic::*]` namespaced attributes. See [RFC 3368 Diagnostic attribute namespace](https://github.com/rust-lang/rfcs/blob/master/text/3368-diagnostic-attribute-namespace.md). +## `tests/ui/diagnostics/` + +Tests for diagnostic output quality, such as path normalization in error messages. + ## `tests/ui/did_you_mean/` Tests for miscellaneous suggestions. diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr index 164bcc7111ca6..6a2952604587e 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr @@ -5,7 +5,7 @@ LL | let y = Mask::<_, _>::splat(false); | ^ ------------ type must be known at this point | note: required by a const generic parameter in `Mask` - --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL + --> $SRC_DIR/portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | LL | let y: Mask<_, N> = Mask::<_, _>::splat(false); @@ -18,7 +18,7 @@ LL | let y = Mask::<_, _>::splat(false); | ^ -------------------------- type must be known at this point | note: required by a const generic parameter in `Mask::::splat` - --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL + --> $SRC_DIR/portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | LL | let y: Mask<_, N> = Mask::<_, _>::splat(false); diff --git a/tests/ui/diagnostics/auxiliary/helper.rs b/tests/ui/diagnostics/auxiliary/helper.rs new file mode 100644 index 0000000000000..83103ab7bd71f --- /dev/null +++ b/tests/ui/diagnostics/auxiliary/helper.rs @@ -0,0 +1,3 @@ +pub fn foo() -> u32 { + "not a u32" +} diff --git a/tests/ui/diagnostics/auxiliary/sub/mod.rs b/tests/ui/diagnostics/auxiliary/sub/mod.rs new file mode 100644 index 0000000000000..dd531abbce552 --- /dev/null +++ b/tests/ui/diagnostics/auxiliary/sub/mod.rs @@ -0,0 +1,2 @@ +#[path = "../helper.rs"] +mod helper; diff --git a/tests/ui/diagnostics/normalize-path.rs b/tests/ui/diagnostics/normalize-path.rs new file mode 100644 index 0000000000000..1b1fb998abf01 --- /dev/null +++ b/tests/ui/diagnostics/normalize-path.rs @@ -0,0 +1,9 @@ +// Verify that diagnostic file paths are lexically normalized. +// Without the fix for #51349, the error location would show +// `auxiliary/sub/../helper.rs` instead of `auxiliary/helper.rs`. +#[path = "auxiliary/sub/mod.rs"] +mod sub; + +fn main() {} + +//~? ERROR mismatched types diff --git a/tests/ui/diagnostics/normalize-path.stderr b/tests/ui/diagnostics/normalize-path.stderr new file mode 100644 index 0000000000000..9eb92e54853e1 --- /dev/null +++ b/tests/ui/diagnostics/normalize-path.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/auxiliary/helper.rs:2:5 + | +LL | pub fn foo() -> u32 { + | --- expected `u32` because of return type +LL | "not a u32" + | ^^^^^^^^^^^ expected `u32`, found `&str` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/imports/ambiguous-2.stderr b/tests/ui/imports/ambiguous-2.stderr index a07f09c41475b..27163cba4009b 100644 --- a/tests/ui/imports/ambiguous-2.stderr +++ b/tests/ui/imports/ambiguous-2.stderr @@ -6,14 +6,14 @@ LL | ambiguous_1::id(); | = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function defined here - --> $DIR/auxiliary/../ambiguous-1.rs:13:13 + --> $DIR/ambiguous-1.rs:13:13 | LL | pub use self::evp::*; | ^^^^^^^^^ = help: consider updating this dependency to resolve this error = help: if updating the dependency does not resolve the problem report the problem to the author of the relevant crate note: `id` could also refer to the function defined here - --> $DIR/auxiliary/../ambiguous-1.rs:15:13 + --> $DIR/ambiguous-1.rs:15:13 | LL | pub use self::handwritten::*; | ^^^^^^^^^^^^^^^^^ @@ -32,14 +32,14 @@ LL | ambiguous_1::id(); | = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function defined here - --> $DIR/auxiliary/../ambiguous-1.rs:13:13 + --> $DIR/ambiguous-1.rs:13:13 | LL | pub use self::evp::*; | ^^^^^^^^^ = help: consider updating this dependency to resolve this error = help: if updating the dependency does not resolve the problem report the problem to the author of the relevant crate note: `id` could also refer to the function defined here - --> $DIR/auxiliary/../ambiguous-1.rs:15:13 + --> $DIR/ambiguous-1.rs:15:13 | LL | pub use self::handwritten::*; | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/imports/ambiguous-4.stderr b/tests/ui/imports/ambiguous-4.stderr index 0d207665ca776..bb6265d5504ac 100644 --- a/tests/ui/imports/ambiguous-4.stderr +++ b/tests/ui/imports/ambiguous-4.stderr @@ -6,14 +6,14 @@ LL | ambiguous_4_extern::id(); | = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function defined here - --> $DIR/auxiliary/../ambiguous-4-extern.rs:13:9 + --> $DIR/ambiguous-4-extern.rs:13:9 | LL | pub use evp::*; | ^^^ = help: consider updating this dependency to resolve this error = help: if updating the dependency does not resolve the problem report the problem to the author of the relevant crate note: `id` could also refer to the function defined here - --> $DIR/auxiliary/../ambiguous-4-extern.rs:14:9 + --> $DIR/ambiguous-4-extern.rs:14:9 | LL | pub use handwritten::*; | ^^^^^^^^^^^ @@ -32,14 +32,14 @@ LL | ambiguous_4_extern::id(); | = note: ambiguous because of multiple glob imports of a name in the same module note: `id` could refer to the function defined here - --> $DIR/auxiliary/../ambiguous-4-extern.rs:13:9 + --> $DIR/ambiguous-4-extern.rs:13:9 | LL | pub use evp::*; | ^^^ = help: consider updating this dependency to resolve this error = help: if updating the dependency does not resolve the problem report the problem to the author of the relevant crate note: `id` could also refer to the function defined here - --> $DIR/auxiliary/../ambiguous-4-extern.rs:14:9 + --> $DIR/ambiguous-4-extern.rs:14:9 | LL | pub use handwritten::*; | ^^^^^^^^^^^