@@ -74,7 +74,7 @@ use std::fmt::Display;
7474use std:: hash:: Hash ;
7575use std:: io:: { self , Read } ;
7676use std:: ops:: { Add , Range , Sub } ;
77- use std:: path:: { Path , PathBuf } ;
77+ use std:: path:: { Component , Path , PathBuf } ;
7878use std:: str:: FromStr ;
7979use std:: sync:: Arc ;
8080use std:: { fmt, iter} ;
@@ -495,11 +495,35 @@ impl RealFileName {
495495 . name
496496 . file_name ( )
497497 . map_or_else ( || "" . into ( ) , |f| f. to_string_lossy ( ) ) ,
498- FileNameDisplayPreference :: Scope ( scope) => self . path ( scope) . to_string_lossy ( ) ,
498+ FileNameDisplayPreference :: Scope ( scope)
499+ | FileNameDisplayPreference :: Diagnostics ( scope) => self . path ( scope) . to_string_lossy ( ) ,
499500 }
500501 }
501502}
502503
504+ /// Lexically normalizes a path by resolving `.` and `..` components without
505+ /// touching the filesystem. Preserves whether the path is relative or absolute.
506+ ///
507+ /// Used for diagnostic display only. `Path::canonicalize` is intentionally avoided
508+ /// because it turns relative paths into absolute ones (see #51349, #83345).
509+ pub ( crate ) fn normalize_path ( path : & Path ) -> PathBuf {
510+ let mut out = PathBuf :: new ( ) ;
511+ for component in path. components ( ) {
512+ match component {
513+ Component :: CurDir => { }
514+ Component :: ParentDir => match out. components ( ) . next_back ( ) {
515+ Some ( Component :: Normal ( _) ) => {
516+ out. pop ( ) ;
517+ }
518+ Some ( Component :: RootDir ) => { }
519+ _ => out. push ( component) ,
520+ } ,
521+ _ => out. push ( component) ,
522+ }
523+ }
524+ if out. as_os_str ( ) . is_empty ( ) { PathBuf :: from ( "." ) } else { out }
525+ }
526+
503527/// Differentiates between real files and common virtual files.
504528#[ derive( Debug , Eq , PartialEq , Clone , Ord , PartialOrd , Hash , Decodable , Encodable ) ]
505529pub enum FileName {
@@ -533,14 +557,36 @@ enum FileNameDisplayPreference {
533557 Local ,
534558 Short ,
535559 Scope ( RemapPathScopeComponents ) ,
560+ Diagnostics ( RemapPathScopeComponents ) ,
561+ }
562+
563+ impl < ' a > FileNameDisplay < ' a > {
564+ fn maybe_normalize < ' s > ( & self , s : Cow < ' s , str > ) -> Cow < ' s , str > {
565+ if !matches ! ( self . display_pref, FileNameDisplayPreference :: Diagnostics ( _) ) {
566+ return s;
567+ }
568+ let path = Path :: new ( s. as_ref ( ) ) ;
569+ if !path. components ( ) . any ( |c| matches ! ( c, Component :: ParentDir | Component :: CurDir ) ) {
570+ return s;
571+ }
572+ Cow :: Owned ( normalize_path ( path) . into_os_string ( ) . to_string_lossy ( ) . into_owned ( ) )
573+ }
574+
575+ pub fn to_string_lossy ( & self ) -> Cow < ' a , str > {
576+ match self . inner {
577+ FileName :: Real ( inner) => self . maybe_normalize ( inner. to_string_lossy ( self . display_pref ) ) ,
578+ _ => Cow :: from ( self . to_string ( ) ) ,
579+ }
580+ }
536581}
537582
538583impl fmt:: Display for FileNameDisplay < ' _ > {
539584 fn fmt ( & self , fmt : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
540585 use FileName :: * ;
541586 match * self . inner {
542587 Real ( ref name) => {
543- write ! ( fmt, "{}" , name. to_string_lossy( self . display_pref) )
588+ let s = self . maybe_normalize ( name. to_string_lossy ( self . display_pref ) ) ;
589+ write ! ( fmt, "{s}" )
544590 }
545591 CfgSpec ( _) => write ! ( fmt, "<cfgspec>" ) ,
546592 MacroExpansion ( _) => write ! ( fmt, "<macro expansion>" ) ,
@@ -554,15 +600,6 @@ impl fmt::Display for FileNameDisplay<'_> {
554600 }
555601}
556602
557- impl < ' a > FileNameDisplay < ' a > {
558- pub fn to_string_lossy ( & self ) -> Cow < ' a , str > {
559- match self . inner {
560- FileName :: Real ( inner) => inner. to_string_lossy ( self . display_pref ) ,
561- _ => Cow :: from ( self . to_string ( ) ) ,
562- }
563- }
564- }
565-
566603impl FileName {
567604 pub fn is_real ( & self ) -> bool {
568605 use FileName :: * ;
@@ -609,6 +646,12 @@ impl FileName {
609646 FileNameDisplay { inner : self , display_pref : FileNameDisplayPreference :: Scope ( scope) }
610647 }
611648
649+ /// Like `display`, but with `.` and `..` resolved lexically. See #51349.
650+ #[ inline]
651+ pub fn display_normalized ( & self , scope : RemapPathScopeComponents ) -> FileNameDisplay < ' _ > {
652+ FileNameDisplay { inner : self , display_pref : FileNameDisplayPreference :: Diagnostics ( scope) }
653+ }
654+
612655 pub fn macro_expansion_source_code ( src : & str ) -> FileName {
613656 let mut hasher = StableHasher :: new ( ) ;
614657 src. hash ( & mut hasher) ;
0 commit comments