From 65520ec802f77ab191423f3a1507d6c0be7cfb9c Mon Sep 17 00:00:00 2001
From: Andy Wang <cbeuw.andy@gmail.com>
Date: Sat, 3 Apr 2021 20:44:18 +0100
Subject: [PATCH 01/12] Add test for --remap-path-prefix on std imports

---
 .../remap_path_prefix/issue-73167-remap-std.rs       | 12 ++++++++++++
 1 file changed, 12 insertions(+)
 create mode 100644 src/test/codegen/remap_path_prefix/issue-73167-remap-std.rs

diff --git a/src/test/codegen/remap_path_prefix/issue-73167-remap-std.rs b/src/test/codegen/remap_path_prefix/issue-73167-remap-std.rs
new file mode 100644
index 0000000000000..56c695d4dedcd
--- /dev/null
+++ b/src/test/codegen/remap_path_prefix/issue-73167-remap-std.rs
@@ -0,0 +1,12 @@
+// ignore-windows
+
+// compile-flags: -g  -C no-prepopulate-passes --remap-path-prefix=/=/the/root/
+
+// Here we check that imported code from std has their path remapped
+
+// CHECK: !DIFile(filename: "{{/the/root/.*/library/std/src/panic.rs}}"
+fn main() {
+    std::thread::spawn(|| {
+        println!("hello");
+    });
+}

From d66506d3532848d5f52301d0c328d79673d7f551 Mon Sep 17 00:00:00 2001
From: Andy Wang <cbeuw.andy@gmail.com>
Date: Mon, 3 May 2021 17:12:10 +0100
Subject: [PATCH 02/12] Normalise remapped sysroot path as $SRC_DIR

---
 src/tools/compiletest/src/runtest.rs | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index ecbaccf744dcd..675d43de8395e 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -3616,6 +3616,12 @@ impl<'test> TestCx<'test> {
             .join("library");
         normalize_path(&src_dir, "$SRC_DIR");
 
+        if let Some(virtual_rust_source_base_dir) =
+            option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from)
+        {
+            normalize_path(&virtual_rust_source_base_dir.join("library"), "$SRC_DIR");
+        }
+
         // Paths into the build directory
         let test_build_dir = &self.config.build_base;
         let parent_build_dir = test_build_dir.parent().unwrap().parent().unwrap().parent().unwrap();

From 6720a370429282e071042af315d712e716d88abf Mon Sep 17 00:00:00 2001
From: Andy Wang <cbeuw.andy@gmail.com>
Date: Sat, 3 Apr 2021 16:59:31 +0100
Subject: [PATCH 03/12] Rename RealFileName::Named to LocalPath and
 Devirtualized to Remapped

---
 compiler/rustc_expand/src/base.rs             |  2 +-
 compiler/rustc_expand/src/expand.rs           |  2 +-
 .../rustc_expand/src/proc_macro_server.rs     |  4 +-
 compiler/rustc_interface/src/passes.rs        |  8 +-
 compiler/rustc_metadata/src/rmeta/decoder.rs  | 14 ++-
 compiler/rustc_metadata/src/rmeta/encoder.rs  | 29 +++---
 compiler/rustc_middle/src/ich/impls_syntax.rs |  3 -
 .../rustc_save_analysis/src/span_utils.rs     |  8 +-
 compiler/rustc_span/src/lib.rs                | 41 +++------
 compiler/rustc_span/src/source_map.rs         | 92 +++++++------------
 compiler/rustc_span/src/source_map/tests.rs   |  2 -
 src/librustdoc/doctest.rs                     |  2 -
 src/librustdoc/json/conversions.rs            |  7 +-
 13 files changed, 82 insertions(+), 132 deletions(-)

diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 5950584281649..f88b05e6c8fdf 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1072,7 +1072,7 @@ impl<'a> ExtCtxt<'a> {
         // after macro expansion (that is, they are unhygienic).
         if !path.is_absolute() {
             let callsite = span.source_callsite();
-            let mut result = match self.source_map().span_to_unmapped_path(callsite) {
+            let mut result = match self.source_map().span_to_filename(callsite) {
                 FileName::Real(name) => name.into_local_path(),
                 FileName::DocTest(path, _) => path,
                 other => {
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 3347c93948ccc..7bb29d20e4a23 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -361,7 +361,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     // FIXME: Avoid visiting the crate as a `Mod` item,
     // make crate a first class expansion target instead.
     pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
-        let file_path = match self.cx.source_map().span_to_unmapped_path(krate.span) {
+        let file_path = match self.cx.source_map().span_to_filename(krate.span) {
             FileName::Real(name) => name.into_local_path(),
             other => PathBuf::from(other.to_string()),
         };
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 1ea26b4eab44b..4089dc88b7b20 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -725,7 +725,7 @@ fn ident_name_compatibility_hack(
         if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
             let source_map = rustc.sess.source_map();
             let filename = source_map.span_to_filename(orig_span);
-            if let FileName::Real(RealFileName::Named(path)) = filename {
+            if let FileName::Real(RealFileName::LocalPath(path)) = filename {
                 let matches_prefix = |prefix, filename| {
                     // Check for a path that ends with 'prefix*/src/<filename>'
                     let mut iter = path.components().rev();
@@ -788,7 +788,7 @@ fn ident_name_compatibility_hack(
                 if macro_name == sym::tuple_from_req && matches_prefix("actix-web", "extract.rs") {
                     let snippet = source_map.span_to_snippet(orig_span);
                     if snippet.as_deref() == Ok("$T") {
-                        if let FileName::Real(RealFileName::Named(macro_path)) =
+                        if let FileName::Real(RealFileName::LocalPath(macro_path)) =
                             source_map.span_to_filename(rustc.def_site)
                         {
                             if macro_path.to_string_lossy().contains("pin-project-internal-0.") {
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 02e62a2cee951..2f91a72e2081b 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -575,7 +575,7 @@ fn write_out_deps(
             .iter()
             .filter(|fmap| fmap.is_real_file())
             .filter(|fmap| !fmap.is_imported())
-            .map(|fmap| escape_dep_filename(&fmap.unmapped_path.as_ref().unwrap_or(&fmap.name)))
+            .map(|fmap| escape_dep_filename(&fmap.name))
             .collect();
 
         if let Some(ref backend) = sess.opts.debugging_opts.codegen_backend {
@@ -587,15 +587,15 @@ fn write_out_deps(
                 for cnum in resolver.cstore().crates_untracked() {
                     let source = resolver.cstore().crate_source_untracked(cnum);
                     if let Some((path, _)) = source.dylib {
-                        let file_name = FileName::Real(RealFileName::Named(path));
+                        let file_name = FileName::Real(RealFileName::LocalPath(path));
                         files.push(escape_dep_filename(&file_name));
                     }
                     if let Some((path, _)) = source.rlib {
-                        let file_name = FileName::Real(RealFileName::Named(path));
+                        let file_name = FileName::Real(RealFileName::LocalPath(path));
                         files.push(escape_dep_filename(&file_name));
                     }
                     if let Some((path, _)) = source.rmeta {
-                        let file_name = FileName::Real(RealFileName::Named(path));
+                        let file_name = FileName::Real(RealFileName::LocalPath(path));
                         files.push(escape_dep_filename(&file_name));
                     }
                 }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 2ade1bb4f95de..74325c62ed3ee 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1635,9 +1635,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
             if let Some(virtual_dir) = virtual_rust_source_base_dir {
                 if let Some(real_dir) = &sess.opts.real_rust_source_base_dir {
                     if let rustc_span::FileName::Real(old_name) = name {
-                        if let rustc_span::RealFileName::Named(one_path) = old_name {
-                            if let Ok(rest) = one_path.strip_prefix(virtual_dir) {
-                                let virtual_name = one_path.clone();
+                        if let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } =
+                            old_name
+                        {
+                            if let Ok(rest) = virtual_name.strip_prefix(virtual_dir) {
+                                let virtual_name = virtual_name.clone();
 
                                 // The std library crates are in
                                 // `$sysroot/lib/rustlib/src/rust/library`, whereas other crates
@@ -1673,7 +1675,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                                     virtual_name.display(),
                                     new_path.display(),
                                 );
-                                let new_name = rustc_span::RealFileName::Devirtualized {
+                                let new_name = rustc_span::RealFileName::Remapped {
                                     local_path: new_path,
                                     virtual_name,
                                 };
@@ -1694,7 +1696,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                     // containing the information we need.
                     let rustc_span::SourceFile {
                         mut name,
-                        name_was_remapped,
                         src_hash,
                         start_pos,
                         end_pos,
@@ -1709,8 +1710,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                     // If this file's path has been remapped to `/rustc/$hash`,
                     // we might be able to reverse that (also see comments above,
                     // on `try_to_translate_virtual_to_real`).
-                    // FIXME(eddyb) we could check `name_was_remapped` here,
-                    // but in practice it seems to be always `false`.
                     try_to_translate_virtual_to_real(&mut name);
 
                     let source_length = (end_pos - start_pos).to_usize();
@@ -1735,7 +1734,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
 
                     let local_version = sess.source_map().new_imported_source_file(
                         name,
-                        name_was_remapped,
                         src_hash,
                         name_hash,
                         source_length,
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index e8f02b8e66f0a..fbae2278f1e1f 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -28,9 +28,12 @@ use rustc_middle::ty::codec::TyEncoder;
 use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
 use rustc_serialize::{opaque, Encodable, Encoder};
 use rustc_session::config::CrateType;
-use rustc_span::hygiene::{ExpnDataEncodeMode, HygieneEncodeContext, MacroKind};
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext};
+use rustc_span::{
+    hygiene::{ExpnDataEncodeMode, HygieneEncodeContext, MacroKind},
+    RealFileName,
+};
 use rustc_target::abi::VariantIdx;
 use std::hash::Hash;
 use std::num::NonZeroUsize;
@@ -485,18 +488,22 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             })
             .map(|(_, source_file)| {
                 let mut adapted = match source_file.name {
-                    // This path of this SourceFile has been modified by
-                    // path-remapping, so we use it verbatim (and avoid
-                    // cloning the whole map in the process).
-                    _ if source_file.name_was_remapped => source_file.clone(),
-
-                    // Otherwise expand all paths to absolute paths because
-                    // any relative paths are potentially relative to a
-                    // wrong directory.
                     FileName::Real(ref name) => {
-                        let name = name.stable_name();
+                        // Expand all local paths to absolute paths because
+                        // any relative paths are potentially relative to a
+                        // wrong directory.
                         let mut adapted = (**source_file).clone();
-                        adapted.name = Path::new(&working_dir).join(name).into();
+                        adapted.name = match name {
+                            RealFileName::LocalPath(local_path) => {
+                                Path::new(&working_dir).join(local_path).into()
+                            }
+                            RealFileName::Remapped { local_path, virtual_name } => {
+                                FileName::Real(RealFileName::Remapped {
+                                    local_path: Path::new(&working_dir).join(local_path),
+                                    virtual_name: virtual_name.clone(),
+                                })
+                            }
+                        };
                         adapted.name_hash = {
                             let mut hasher: StableHasher = StableHasher::new();
                             adapted.name.hash(&mut hasher);
diff --git a/compiler/rustc_middle/src/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs
index b93b25d6b5c9a..2d8f661ef59e9 100644
--- a/compiler/rustc_middle/src/ich/impls_syntax.rs
+++ b/compiler/rustc_middle/src/ich/impls_syntax.rs
@@ -61,8 +61,6 @@ impl<'a> HashStable<StableHashingContext<'a>> for SourceFile {
         let SourceFile {
             name: _, // We hash the smaller name_hash instead of this
             name_hash,
-            name_was_remapped,
-            unmapped_path: _,
             cnum,
             // Do not hash the source as it is not encoded
             src: _,
@@ -77,7 +75,6 @@ impl<'a> HashStable<StableHashingContext<'a>> for SourceFile {
         } = *self;
 
         (name_hash as u64).hash_stable(hcx, hasher);
-        name_was_remapped.hash_stable(hcx, hasher);
 
         src_hash.hash_stable(hcx, hasher);
 
diff --git a/compiler/rustc_save_analysis/src/span_utils.rs b/compiler/rustc_save_analysis/src/span_utils.rs
index edcd492577374..5e16a427f3fa3 100644
--- a/compiler/rustc_save_analysis/src/span_utils.rs
+++ b/compiler/rustc_save_analysis/src/span_utils.rs
@@ -16,8 +16,7 @@ impl<'a> SpanUtils<'a> {
 
     pub fn make_filename_string(&self, file: &SourceFile) -> String {
         match &file.name {
-            FileName::Real(name) if !file.name_was_remapped => {
-                let path = name.local_path();
+            FileName::Real(RealFileName::LocalPath(path)) => {
                 if path.is_absolute() {
                     self.sess
                         .source_map()
@@ -30,8 +29,11 @@ impl<'a> SpanUtils<'a> {
                     self.sess.working_dir.0.join(&path).display().to_string()
                 }
             }
-            // If the file name is already remapped, we assume the user
+            // If the file name was remapped, we assume the user
             // configured it the way they wanted to, so use that directly
+            FileName::Real(RealFileName::Remapped { local_path: _, virtual_name }) => {
+                virtual_name.display().to_string()
+            }
             filename => filename.to_string(),
         }
     }
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index d30236ec3eccc..44617b5aa0026 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -113,17 +113,16 @@ pub fn with_default_session_globals<R>(f: impl FnOnce() -> R) -> R {
 // deserialization.
 scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
 
-// FIXME: Perhaps this should not implement Rustc{Decodable, Encodable}
-//
 // FIXME: We should use this enum or something like it to get rid of the
 // use of magic `/rust/1.x/...` paths across the board.
 #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
 #[derive(HashStable_Generic, Decodable, Encodable)]
 pub enum RealFileName {
-    Named(PathBuf),
-    /// For de-virtualized paths (namely paths into libstd that have been mapped
-    /// to the appropriate spot on the local host's file system),
-    Devirtualized {
+    LocalPath(PathBuf),
+    /// For remapped paths (namely paths into libstd that have been mapped
+    /// to the appropriate spot on the local host's file system, and local file
+    /// system paths that have been remapped with `FilePathMapping`),
+    Remapped {
         /// `local_path` is the (host-dependent) local path to the file.
         local_path: PathBuf,
         /// `virtual_name` is the stable path rustc will store internally within
@@ -137,8 +136,8 @@ impl RealFileName {
     /// Avoid embedding this in build artifacts; see `stable_name()` for that.
     pub fn local_path(&self) -> &Path {
         match self {
-            RealFileName::Named(p)
-            | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => &p,
+            RealFileName::LocalPath(p)
+            | RealFileName::Remapped { local_path: p, virtual_name: _ } => &p,
         }
     }
 
@@ -146,19 +145,19 @@ impl RealFileName {
     /// Avoid embedding this in build artifacts; see `stable_name()` for that.
     pub fn into_local_path(self) -> PathBuf {
         match self {
-            RealFileName::Named(p)
-            | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => p,
+            RealFileName::LocalPath(p)
+            | RealFileName::Remapped { local_path: p, virtual_name: _ } => p,
         }
     }
 
     /// Returns the path suitable for embedding into build artifacts. Note that
-    /// a virtualized path will not correspond to a valid file system path; see
+    /// a remapped path will not correspond to a valid file system path; see
     /// `local_path()` for something that is more likely to return paths into the
     /// local host file system.
     pub fn stable_name(&self) -> &Path {
         match self {
-            RealFileName::Named(p)
-            | RealFileName::Devirtualized { local_path: _, virtual_name: p } => &p,
+            RealFileName::LocalPath(p)
+            | RealFileName::Remapped { local_path: _, virtual_name: p } => &p,
         }
     }
 }
@@ -214,7 +213,7 @@ impl std::fmt::Display for FileName {
 impl From<PathBuf> for FileName {
     fn from(p: PathBuf) -> Self {
         assert!(!p.to_string_lossy().ends_with('>'));
-        FileName::Real(RealFileName::Named(p))
+        FileName::Real(RealFileName::LocalPath(p))
     }
 }
 
@@ -1124,11 +1123,6 @@ pub struct SourceFile {
     /// originate from files has names between angle brackets by convention
     /// (e.g., `<anon>`).
     pub name: FileName,
-    /// `true` if the `name` field above has been modified by `--remap-path-prefix`.
-    pub name_was_remapped: bool,
-    /// The unmapped path of the file that the source came from.
-    /// Set to `None` if the `SourceFile` was imported from an external crate.
-    pub unmapped_path: Option<FileName>,
     /// The complete source code.
     pub src: Option<Lrc<String>>,
     /// The source code's hash.
@@ -1158,7 +1152,6 @@ impl<S: Encoder> Encodable<S> for SourceFile {
     fn encode(&self, s: &mut S) -> Result<(), S::Error> {
         s.emit_struct("SourceFile", 8, |s| {
             s.emit_struct_field("name", 0, |s| self.name.encode(s))?;
-            s.emit_struct_field("name_was_remapped", 1, |s| self.name_was_remapped.encode(s))?;
             s.emit_struct_field("src_hash", 2, |s| self.src_hash.encode(s))?;
             s.emit_struct_field("start_pos", 3, |s| self.start_pos.encode(s))?;
             s.emit_struct_field("end_pos", 4, |s| self.end_pos.encode(s))?;
@@ -1233,8 +1226,6 @@ impl<D: Decoder> Decodable<D> for SourceFile {
     fn decode(d: &mut D) -> Result<SourceFile, D::Error> {
         d.read_struct("SourceFile", 8, |d| {
             let name: FileName = d.read_struct_field("name", 0, |d| Decodable::decode(d))?;
-            let name_was_remapped: bool =
-                d.read_struct_field("name_was_remapped", 1, |d| Decodable::decode(d))?;
             let src_hash: SourceFileHash =
                 d.read_struct_field("src_hash", 2, |d| Decodable::decode(d))?;
             let start_pos: BytePos =
@@ -1278,8 +1269,6 @@ impl<D: Decoder> Decodable<D> for SourceFile {
             let cnum: CrateNum = d.read_struct_field("cnum", 10, |d| Decodable::decode(d))?;
             Ok(SourceFile {
                 name,
-                name_was_remapped,
-                unmapped_path: None,
                 start_pos,
                 end_pos,
                 src: None,
@@ -1307,8 +1296,6 @@ impl fmt::Debug for SourceFile {
 impl SourceFile {
     pub fn new(
         name: FileName,
-        name_was_remapped: bool,
-        unmapped_path: FileName,
         mut src: String,
         start_pos: BytePos,
         hash_kind: SourceFileHashAlgorithm,
@@ -1330,8 +1317,6 @@ impl SourceFile {
 
         SourceFile {
             name,
-            name_was_remapped,
-            unmapped_path: Some(unmapped_path),
             src: Some(Lrc::new(src)),
             src_hash,
             external_src: Lock::new(ExternalSource::Unneeded),
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index f612d1425b932..03be11f828b4b 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -15,11 +15,11 @@ pub use crate::*;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock};
-use std::cmp;
-use std::convert::TryFrom;
 use std::hash::Hash;
 use std::path::{Path, PathBuf};
 use std::sync::atomic::Ordering;
+use std::{clone::Clone, cmp};
+use std::{convert::TryFrom, unreachable};
 
 use std::fs;
 use std::io;
@@ -127,30 +127,16 @@ pub struct StableSourceFileId(u128);
 // StableSourceFileId, perhaps built atop source_file.name_hash.
 impl StableSourceFileId {
     pub fn new(source_file: &SourceFile) -> StableSourceFileId {
-        StableSourceFileId::new_from_pieces(
-            &source_file.name,
-            source_file.name_was_remapped,
-            source_file.unmapped_path.as_ref(),
-        )
+        StableSourceFileId::new_from_name(&source_file.name)
     }
 
-    fn new_from_pieces(
-        name: &FileName,
-        name_was_remapped: bool,
-        unmapped_path: Option<&FileName>,
-    ) -> StableSourceFileId {
+    fn new_from_name(name: &FileName) -> StableSourceFileId {
         let mut hasher = StableHasher::new();
 
-        if let FileName::Real(real_name) = name {
-            // rust-lang/rust#70924: Use the stable (virtualized) name when
-            // available. (We do not want artifacts from transient file system
-            // paths for libstd to leak into our build artifacts.)
-            real_name.stable_name().hash(&mut hasher)
-        } else {
-            name.hash(&mut hasher);
-        }
-        name_was_remapped.hash(&mut hasher);
-        unmapped_path.hash(&mut hasher);
+        // If name was remapped, we need to take both the local path
+        // and stablised path into account, in case two different paths were
+        // mapped to the same
+        name.hash(&mut hasher);
 
         StableSourceFileId(hasher.finish())
     }
@@ -283,35 +269,15 @@ impl SourceMap {
 
     fn try_new_source_file(
         &self,
-        mut filename: FileName,
+        filename: FileName,
         src: String,
     ) -> Result<Lrc<SourceFile>, OffsetOverflowError> {
-        // The path is used to determine the directory for loading submodules and
-        // include files, so it must be before remapping.
         // Note that filename may not be a valid path, eg it may be `<anon>` etc,
         // but this is okay because the directory determined by `path.pop()` will
         // be empty, so the working directory will be used.
-        let unmapped_path = filename.clone();
-
-        let was_remapped;
-        if let FileName::Real(real_filename) = &mut filename {
-            match real_filename {
-                RealFileName::Named(path_to_be_remapped)
-                | RealFileName::Devirtualized {
-                    local_path: path_to_be_remapped,
-                    virtual_name: _,
-                } => {
-                    let mapped = self.path_mapping.map_prefix(path_to_be_remapped.clone());
-                    was_remapped = mapped.1;
-                    *path_to_be_remapped = mapped.0;
-                }
-            }
-        } else {
-            was_remapped = false;
-        }
+        let (filename, _) = self.path_mapping.map_filename_prefix(&filename);
 
-        let file_id =
-            StableSourceFileId::new_from_pieces(&filename, was_remapped, Some(&unmapped_path));
+        let file_id = StableSourceFileId::new_from_name(&filename);
 
         let lrc_sf = match self.source_file_by_stable_id(file_id) {
             Some(lrc_sf) => lrc_sf,
@@ -320,8 +286,6 @@ impl SourceMap {
 
                 let source_file = Lrc::new(SourceFile::new(
                     filename,
-                    was_remapped,
-                    unmapped_path,
                     src,
                     Pos::from_usize(start_pos),
                     self.hash_kind,
@@ -345,7 +309,6 @@ impl SourceMap {
     pub fn new_imported_source_file(
         &self,
         filename: FileName,
-        name_was_remapped: bool,
         src_hash: SourceFileHash,
         name_hash: u128,
         source_len: usize,
@@ -382,8 +345,6 @@ impl SourceMap {
 
         let source_file = Lrc::new(SourceFile {
             name: filename,
-            name_was_remapped,
-            unmapped_path: None,
             src: None,
             src_hash,
             external_src: Lock::new(ExternalSource::Foreign {
@@ -474,14 +435,6 @@ impl SourceMap {
         self.lookup_char_pos(sp.lo()).file.name.clone()
     }
 
-    pub fn span_to_unmapped_path(&self, sp: Span) -> FileName {
-        self.lookup_char_pos(sp.lo())
-            .file
-            .unmapped_path
-            .clone()
-            .expect("`SourceMap::span_to_unmapped_path` called for imported `SourceFile`?")
-    }
-
     pub fn is_multiline(&self, sp: Span) -> bool {
         let lo = self.lookup_char_pos(sp.lo());
         let hi = self.lookup_char_pos(sp.hi());
@@ -1046,9 +999,26 @@ impl FilePathMapping {
     fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) {
         match file {
             FileName::Real(realfile) => {
-                let path = realfile.local_path();
-                let (path, mapped) = self.map_prefix(path.to_path_buf());
-                (FileName::Real(RealFileName::Named(path)), mapped)
+                // If the file is the Name variant with only local_path, then clearly we want to map that
+                // to a virtual_name
+                // If the file is already remapped, then we want to map virtual_name further
+                // but we leave local_path alone
+                let path = realfile.stable_name();
+                let (mapped_path, mapped) = self.map_prefix(path.to_path_buf());
+                if mapped {
+                    let mapped_realfile = match realfile {
+                        RealFileName::LocalPath(local_path)
+                        | RealFileName::Remapped { local_path, virtual_name: _ } => {
+                            RealFileName::Remapped {
+                                local_path: local_path.clone(),
+                                virtual_name: mapped_path,
+                            }
+                        }
+                    };
+                    (FileName::Real(mapped_realfile), mapped)
+                } else {
+                    unreachable!("attempted to remap an already remapped filename");
+                }
             }
             other => (other.clone(), false),
         }
diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs
index 7d814f1d82c11..ee87ef0b5e5e7 100644
--- a/compiler/rustc_span/src/source_map/tests.rs
+++ b/compiler/rustc_span/src/source_map/tests.rs
@@ -229,7 +229,6 @@ fn t10() {
 
     let SourceFile {
         name,
-        name_was_remapped,
         src_hash,
         start_pos,
         end_pos,
@@ -243,7 +242,6 @@ fn t10() {
 
     let imported_src_file = sm.new_imported_source_file(
         name,
-        name_was_remapped,
         src_hash,
         name_hash,
         (end_pos - start_pos).to_usize(),
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 466d1b65406cd..9b945cd42cfb6 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -883,8 +883,6 @@ impl Tester for Collector {
             self.compiling_test_count.fetch_add(1, Ordering::SeqCst);
         }
 
-        // FIXME(#44940): if doctests ever support path remapping, then this filename
-        // needs to be the result of `SourceMap::span_to_unmapped_path`.
         let path = match &filename {
             FileName::Real(path) => path.local_path().to_path_buf(),
             _ => PathBuf::from(r"doctest.rs"),
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 8ca6342462fc4..88e369e1babc2 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -66,12 +66,7 @@ impl JsonRenderer<'_> {
                 let hi = span.hi(self.sess());
                 let lo = span.lo(self.sess());
                 Some(Span {
-                    filename: match name {
-                        rustc_span::RealFileName::Named(path) => path,
-                        rustc_span::RealFileName::Devirtualized { local_path, virtual_name: _ } => {
-                            local_path
-                        }
-                    },
+                    filename: name.into_local_path(),
                     begin: (lo.line, lo.col.to_usize()),
                     end: (hi.line, hi.col.to_usize()),
                 })

From 9e0426d7842c4a603237789b59e6c491d2dd3b4a Mon Sep 17 00:00:00 2001
From: Andy Wang <qian.wang19@imperial.ac.uk>
Date: Fri, 9 Apr 2021 00:54:51 +0100
Subject: [PATCH 04/12] Make local_path in RealFileName::Remapped Option to be
 removed in exported metadata

---
 compiler/rustc_expand/src/base.rs             |  4 +-
 compiler/rustc_expand/src/expand.rs           |  4 +-
 .../rustc_expand/src/proc_macro_server.rs     |  1 +
 compiler/rustc_metadata/src/rmeta/decoder.rs  |  2 +-
 compiler/rustc_metadata/src/rmeta/encoder.rs  |  5 +-
 compiler/rustc_span/src/lib.rs                | 66 +++++++++++++++----
 compiler/rustc_span/src/source_map.rs         | 35 +++++-----
 src/librustdoc/doctest.rs                     | 15 ++++-
 src/librustdoc/html/render/context.rs         | 17 +++--
 src/librustdoc/html/sources.rs                |  8 ++-
 src/librustdoc/json/conversions.rs            | 18 +++--
 11 files changed, 123 insertions(+), 52 deletions(-)

diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index f88b05e6c8fdf..5c83d6c7ad598 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1073,7 +1073,9 @@ impl<'a> ExtCtxt<'a> {
         if !path.is_absolute() {
             let callsite = span.source_callsite();
             let mut result = match self.source_map().span_to_filename(callsite) {
-                FileName::Real(name) => name.into_local_path(),
+                FileName::Real(name) => name
+                    .into_local_path()
+                    .expect("attempting to resolve a file path in an external file"),
                 FileName::DocTest(path, _) => path,
                 other => {
                     return Err(self.struct_span_err(
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 7bb29d20e4a23..03910f4e18d9a 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -362,7 +362,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     // make crate a first class expansion target instead.
     pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
         let file_path = match self.cx.source_map().span_to_filename(krate.span) {
-            FileName::Real(name) => name.into_local_path(),
+            FileName::Real(name) => name
+                .into_local_path()
+                .expect("attempting to resolve a file path in an external file"),
             other => PathBuf::from(other.to_string()),
         };
         let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 4089dc88b7b20..f91c0d8313824 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -623,6 +623,7 @@ impl server::SourceFile for Rustc<'_> {
         match file.name {
             FileName::Real(ref name) => name
                 .local_path()
+                .expect("attempting to get a file path in an imported file in `proc_macro::SourceFile::path`")
                 .to_str()
                 .expect("non-UTF8 file path in `proc_macro::SourceFile::path`")
                 .to_string(),
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 74325c62ed3ee..4cb90d5a6d618 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1676,7 +1676,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                                     new_path.display(),
                                 );
                                 let new_name = rustc_span::RealFileName::Remapped {
-                                    local_path: new_path,
+                                    local_path: Some(new_path),
                                     virtual_name,
                                 };
                                 *old_name = new_name;
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index fbae2278f1e1f..9a398d902a781 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -497,9 +497,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                             RealFileName::LocalPath(local_path) => {
                                 Path::new(&working_dir).join(local_path).into()
                             }
-                            RealFileName::Remapped { local_path, virtual_name } => {
+                            RealFileName::Remapped { local_path: _, virtual_name } => {
                                 FileName::Real(RealFileName::Remapped {
-                                    local_path: Path::new(&working_dir).join(local_path),
+                                    // We do not want any local path to be exported into metadata
+                                    local_path: None,
                                     virtual_name: virtual_name.clone(),
                                 })
                             }
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 44617b5aa0026..e5e6f31886c4b 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -115,38 +115,80 @@ scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
 
 // FIXME: We should use this enum or something like it to get rid of the
 // use of magic `/rust/1.x/...` paths across the board.
-#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
-#[derive(HashStable_Generic, Decodable, Encodable)]
+#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd)]
+#[derive(HashStable_Generic, Decodable)]
 pub enum RealFileName {
     LocalPath(PathBuf),
     /// For remapped paths (namely paths into libstd that have been mapped
     /// to the appropriate spot on the local host's file system, and local file
     /// system paths that have been remapped with `FilePathMapping`),
     Remapped {
-        /// `local_path` is the (host-dependent) local path to the file.
-        local_path: PathBuf,
+        /// `local_path` is the (host-dependent) local path to the file. This is
+        /// None if the file was imported from another crate
+        local_path: Option<PathBuf>,
         /// `virtual_name` is the stable path rustc will store internally within
         /// build artifacts.
         virtual_name: PathBuf,
     },
 }
 
+impl Hash for RealFileName {
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        // To prevent #70924 from happening again we should only hash the
+        // remapped (virtualized) path if that exists. This is because
+        // virtualized paths to sysroot crates (/rust/$hash or /rust/$version)
+        // remain stable even if the corresponding local_path changes
+        self.remapped_path_if_available().hash(state)
+    }
+}
+
+// This is functionally identical to #[derive(Encodable)], with the exception of
+// an added assert statement
+impl<S: Encoder> Encodable<S> for RealFileName {
+    fn encode(&self, encoder: &mut S) -> Result<(), S::Error> {
+        encoder.emit_enum("RealFileName", |encoder| match *self {
+            RealFileName::LocalPath(ref local_path) => {
+                encoder.emit_enum_variant("LocalPath", 0, 1, |encoder| {
+                    Ok({
+                        encoder.emit_enum_variant_arg(0, |encoder| local_path.encode(encoder))?;
+                    })
+                })
+            }
+
+            RealFileName::Remapped { ref local_path, ref virtual_name } => encoder
+                .emit_enum_variant("Remapped", 1, 2, |encoder| {
+                    // For privacy and build reproducibility, we must not embed host-dependant path in artifacts
+                    // if they have been remapped by --remap-path-prefix
+                    assert!(local_path.is_none());
+                    Ok({
+                        encoder.emit_enum_variant_arg(0, |encoder| local_path.encode(encoder))?;
+                        encoder.emit_enum_variant_arg(1, |encoder| virtual_name.encode(encoder))?;
+                    })
+                }),
+        })
+    }
+}
+
 impl RealFileName {
-    /// Returns the path suitable for reading from the file system on the local host.
+    /// Returns the path suitable for reading from the file system on the local host,
+    /// if this information exists.
     /// Avoid embedding this in build artifacts; see `stable_name()` for that.
-    pub fn local_path(&self) -> &Path {
+    pub fn local_path(&self) -> Option<&Path> {
         match self {
-            RealFileName::LocalPath(p)
-            | RealFileName::Remapped { local_path: p, virtual_name: _ } => &p,
+            RealFileName::LocalPath(p) => Some(p),
+            RealFileName::Remapped { local_path: p, virtual_name: _ } => {
+                p.as_ref().map(PathBuf::as_path)
+            }
         }
     }
 
-    /// Returns the path suitable for reading from the file system on the local host.
+    /// Returns the path suitable for reading from the file system on the local host,
+    /// if this information exists.
     /// Avoid embedding this in build artifacts; see `stable_name()` for that.
-    pub fn into_local_path(self) -> PathBuf {
+    pub fn into_local_path(self) -> Option<PathBuf> {
         match self {
-            RealFileName::LocalPath(p)
-            | RealFileName::Remapped { local_path: p, virtual_name: _ } => p,
+            RealFileName::LocalPath(p) => Some(p),
+            RealFileName::Remapped { local_path: p, virtual_name: _ } => p,
         }
     }
 
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 03be11f828b4b..4e60d071c6873 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -133,9 +133,6 @@ impl StableSourceFileId {
     fn new_from_name(name: &FileName) -> StableSourceFileId {
         let mut hasher = StableHasher::new();
 
-        // If name was remapped, we need to take both the local path
-        // and stablised path into account, in case two different paths were
-        // mapped to the same
         name.hash(&mut hasher);
 
         StableSourceFileId(hasher.finish())
@@ -954,7 +951,13 @@ impl SourceMap {
     }
     pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
         source_file.add_external_src(|| match source_file.name {
-            FileName::Real(ref name) => self.file_loader.read_file(name.local_path()).ok(),
+            FileName::Real(ref name) => {
+                if let Some(local_path) = name.local_path() {
+                    self.file_loader.read_file(local_path).ok()
+                } else {
+                    None
+                }
+            }
             _ => None,
         })
     }
@@ -999,23 +1002,17 @@ impl FilePathMapping {
     fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) {
         match file {
             FileName::Real(realfile) => {
-                // If the file is the Name variant with only local_path, then clearly we want to map that
-                // to a virtual_name
-                // If the file is already remapped, then we want to map virtual_name further
-                // but we leave local_path alone
-                let path = realfile.stable_name();
-                let (mapped_path, mapped) = self.map_prefix(path.to_path_buf());
-                if mapped {
-                    let mapped_realfile = match realfile {
-                        RealFileName::LocalPath(local_path)
-                        | RealFileName::Remapped { local_path, virtual_name: _ } => {
-                            RealFileName::Remapped {
-                                local_path: local_path.clone(),
-                                virtual_name: mapped_path,
-                            }
+                if let RealFileName::LocalPath(local_path) = realfile {
+                    let (mapped_path, mapped) = self.map_prefix(local_path.to_path_buf());
+                    let realfile = if mapped {
+                        RealFileName::Remapped {
+                            local_path: Some(local_path.clone()),
+                            virtual_name: mapped_path,
                         }
+                    } else {
+                        realfile.clone()
                     };
-                    (FileName::Real(mapped_realfile), mapped)
+                    (FileName::Real(realfile), mapped)
                 } else {
                     unreachable!("attempted to remap an already remapped filename");
                 }
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 9b945cd42cfb6..576f27f8f5c5c 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -852,8 +852,10 @@ impl Collector {
             let filename = source_map.span_to_filename(self.position);
             if let FileName::Real(ref filename) = filename {
                 if let Ok(cur_dir) = env::current_dir() {
-                    if let Ok(path) = filename.local_path().strip_prefix(&cur_dir) {
-                        return path.to_owned().into();
+                    if let Some(local_path) = filename.local_path() {
+                        if let Ok(path) = local_path.strip_prefix(&cur_dir) {
+                            return path.to_owned().into();
+                        }
                     }
                 }
             }
@@ -884,7 +886,14 @@ impl Tester for Collector {
         }
 
         let path = match &filename {
-            FileName::Real(path) => path.local_path().to_path_buf(),
+            FileName::Real(path) => {
+                if let Some(local_path) = path.local_path() {
+                    local_path.to_path_buf()
+                } else {
+                    // Somehow we got the filename from the metadata of another crate, should never happen
+                    PathBuf::from(r"doctest.rs")
+                }
+            }
             _ => PathBuf::from(r"doctest.rs"),
         };
 
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 293c0a40fa799..95f3a32e41f89 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -290,7 +290,7 @@ impl<'tcx> Context<'tcx> {
 
         // We can safely ignore synthetic `SourceFile`s.
         let file = match item.span(self.tcx()).filename(self.sess()) {
-            FileName::Real(ref path) => path.local_path().to_path_buf(),
+            FileName::Real(ref path) => path.local_path_if_available().to_path_buf(),
             _ => return None,
         };
         let file = &file;
@@ -376,10 +376,17 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         } = options;
 
         let src_root = match krate.src {
-            FileName::Real(ref p) => match p.local_path().parent() {
-                Some(p) => p.to_path_buf(),
-                None => PathBuf::new(),
-            },
+            FileName::Real(ref p) => {
+                if let Some(local_path) = p.local_path() {
+                    match local_path.parent() {
+                        Some(p) => p.to_path_buf(),
+                        None => PathBuf::new(),
+                    }
+                } else {
+                    // Somehow we got the filename from the metadata of another crate, should never happen
+                    PathBuf::new()
+                }
+            }
             _ => PathBuf::new(),
         };
         // If user passed in `--playground-url` arg, we fill in crate name here
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 3d4d8df0a7198..9753179b6e18d 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -76,7 +76,13 @@ impl SourceCollector<'_, 'tcx> {
     /// Renders the given filename into its corresponding HTML source file.
     fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
         let p = match *filename {
-            FileName::Real(ref file) => file.local_path().to_path_buf(),
+            FileName::Real(ref file) => {
+                if let Some(local_path) = file.local_path() {
+                    local_path.to_path_buf()
+                } else {
+                    return Ok(());
+                }
+            }
             _ => return Ok(()),
         };
         if self.scx.local_sources.contains_key(&*p) {
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 88e369e1babc2..e0009bf264a13 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -63,13 +63,17 @@ impl JsonRenderer<'_> {
     fn convert_span(&self, span: clean::Span) -> Option<Span> {
         match span.filename(self.sess()) {
             rustc_span::FileName::Real(name) => {
-                let hi = span.hi(self.sess());
-                let lo = span.lo(self.sess());
-                Some(Span {
-                    filename: name.into_local_path(),
-                    begin: (lo.line, lo.col.to_usize()),
-                    end: (hi.line, hi.col.to_usize()),
-                })
+                if let Some(local_path) = name.into_local_path() {
+                    let hi = span.hi(self.sess());
+                    let lo = span.lo(self.sess());
+                    Some(Span {
+                        filename: local_path,
+                        begin: (lo.line, lo.col.to_usize()),
+                        end: (hi.line, hi.col.to_usize()),
+                    })
+                } else {
+                    None
+                }
             }
             _ => None,
         }

From 04079190835c04e8c671daef492144dfe06564ab Mon Sep 17 00:00:00 2001
From: Andy Wang <qian.wang19@imperial.ac.uk>
Date: Sat, 10 Apr 2021 13:48:06 +0100
Subject: [PATCH 05/12] Use RealFileName for Session::working_dir as it may
 also be remapped

---
 .../rustc_codegen_cranelift/src/debuginfo/mod.rs   |  2 +-
 .../rustc_codegen_llvm/src/debuginfo/metadata.rs   |  4 ++--
 compiler/rustc_metadata/src/rmeta/encoder.rs       |  2 +-
 compiler/rustc_save_analysis/src/dump_visitor.rs   |  2 +-
 compiler/rustc_save_analysis/src/span_utils.rs     |  2 +-
 compiler/rustc_session/src/session.rs              | 14 +++++++++-----
 6 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
index dc8bc8d9cb741..10d3651789b3a 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
@@ -64,7 +64,7 @@ impl<'tcx> DebugContext<'tcx> {
         // FIXME: how to get version when building out of tree?
         // Normally this would use option_env!("CFG_VERSION").
         let producer = format!("cg_clif (rustc {})", "unknown version");
-        let comp_dir = tcx.sess.working_dir.0.to_string_lossy().into_owned();
+        let comp_dir = tcx.sess.working_dir.stable_name().to_string_lossy().into_owned();
         let (name, file_info) = match tcx.sess.local_crate_source_file.clone() {
             Some(path) => {
                 let name = path.to_string_lossy().into_owned();
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index e6fa852155b51..ccef95308500f 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -764,7 +764,7 @@ pub fn file_metadata(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll
     let hash = Some(&source_file.src_hash);
     let file_name = Some(source_file.name.to_string());
     let directory = if source_file.is_real_file() && !source_file.is_imported() {
-        Some(cx.sess().working_dir.0.to_string_lossy().to_string())
+        Some(cx.sess().working_dir.stable_name().to_string_lossy().to_string())
     } else {
         // If the path comes from an upstream crate we assume it has been made
         // independent of the compiler's working directory one way or another.
@@ -992,7 +992,7 @@ pub fn compile_unit_metadata(
     let producer = format!("clang LLVM ({})", rustc_producer);
 
     let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
-    let work_dir = tcx.sess.working_dir.0.to_string_lossy();
+    let work_dir = tcx.sess.working_dir.stable_name().to_string_lossy();
     let flags = "\0";
     let out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory;
     let split_name = if tcx.sess.target_can_use_split_dwarf() {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 9a398d902a781..9253f421cfe11 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -469,7 +469,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let source_map = self.tcx.sess.source_map();
         let all_source_files = source_map.files();
 
-        let (working_dir, _cwd_remapped) = self.tcx.sess.working_dir.clone();
+        let working_dir = self.tcx.sess.working_dir.stable_name();
         // By replacing the `Option` with `None`, we ensure that we can't
         // accidentally serialize any more `Span`s after the source map encoding
         // is done.
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index 12c77e0c8a60d..ab1b4db6293bd 100644
--- a/compiler/rustc_save_analysis/src/dump_visitor.rs
+++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -190,7 +190,7 @@ impl<'tcx> DumpVisitor<'tcx> {
         };
 
         let data = CompilationOptions {
-            directory: self.tcx.sess.working_dir.0.clone(),
+            directory: self.tcx.sess.working_dir.stable_name().into(),
             program,
             arguments,
             output: self.save_ctxt.compilation_output(crate_name),
diff --git a/compiler/rustc_save_analysis/src/span_utils.rs b/compiler/rustc_save_analysis/src/span_utils.rs
index 5e16a427f3fa3..bcda3a6d006f1 100644
--- a/compiler/rustc_save_analysis/src/span_utils.rs
+++ b/compiler/rustc_save_analysis/src/span_utils.rs
@@ -26,7 +26,7 @@ impl<'a> SpanUtils<'a> {
                         .display()
                         .to_string()
                 } else {
-                    self.sess.working_dir.0.join(&path).display().to_string()
+                    self.sess.working_dir.stable_name().join(&path).display().to_string()
                 }
             }
             // If the file name was remapped, we assume the user
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index e7dfc4b8c4128..724ad35855e47 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -23,8 +23,8 @@ use rustc_errors::registry::Registry;
 use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported};
 use rustc_lint_defs::FutureBreakage;
 pub use rustc_span::crate_disambiguator::CrateDisambiguator;
-use rustc_span::edition::Edition;
 use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span};
+use rustc_span::{edition::Edition, RealFileName};
 use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
 use rustc_target::asm::InlineAsmArch;
 use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
@@ -125,9 +125,8 @@ pub struct Session {
     /// The name of the root source file of the crate, in the local file system.
     /// `None` means that there is no source file.
     pub local_crate_source_file: Option<PathBuf>,
-    /// The directory the compiler has been executed in plus a flag indicating
-    /// if the value stored here has been affected by path remapping.
-    pub working_dir: (PathBuf, bool),
+    /// The directory the compiler has been executed in
+    pub working_dir: RealFileName,
 
     /// Set of `(DiagnosticId, Option<Span>, message)` tuples tracking
     /// (sub)diagnostics that have been set once, but should not be set again,
@@ -1361,7 +1360,12 @@ pub fn build_session(
     let working_dir = env::current_dir().unwrap_or_else(|e| {
         parse_sess.span_diagnostic.fatal(&format!("Current directory is invalid: {}", e)).raise()
     });
-    let working_dir = file_path_mapping.map_prefix(working_dir);
+    let (path, remapped) = file_path_mapping.map_prefix(working_dir.clone());
+    let working_dir = if remapped {
+        RealFileName::Remapped { local_path: Some(working_dir), virtual_name: path }
+    } else {
+        RealFileName::LocalPath(path)
+    };
 
     let cgu_reuse_tracker = if sopts.debugging_opts.query_dep_graph {
         CguReuseTracker::new()

From e5445f3722ff0e97681139480931166f791c28a7 Mon Sep 17 00:00:00 2001
From: Andy Wang <qian.wang19@imperial.ac.uk>
Date: Wed, 14 Apr 2021 15:33:46 +0100
Subject: [PATCH 06/12] Emit RealFileName::Remapped on expanded relative path
 if working_dir has been remapped

---
 compiler/rustc_metadata/src/rmeta/encoder.rs | 32 ++++++++++++++------
 1 file changed, 22 insertions(+), 10 deletions(-)

diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 9253f421cfe11..a3e0dd8e3b9b6 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -469,7 +469,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let source_map = self.tcx.sess.source_map();
         let all_source_files = source_map.files();
 
-        let working_dir = self.tcx.sess.working_dir.stable_name();
         // By replacing the `Option` with `None`, we ensure that we can't
         // accidentally serialize any more `Span`s after the source map encoding
         // is done.
@@ -488,23 +487,36 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             })
             .map(|(_, source_file)| {
                 let mut adapted = match source_file.name {
-                    FileName::Real(ref name) => {
-                        // Expand all local paths to absolute paths because
-                        // any relative paths are potentially relative to a
-                        // wrong directory.
+                    FileName::Real(ref realname) => {
                         let mut adapted = (**source_file).clone();
-                        adapted.name = match name {
+                        adapted.name = FileName::Real(match realname {
                             RealFileName::LocalPath(local_path) => {
-                                Path::new(&working_dir).join(local_path).into()
+                                // Prepend path of working directory onto local path.
+                                // because relative paths are potentially relative to a
+                                // wrong directory.
+                                let working_dir = &self.tcx.sess.working_dir;
+                                if let RealFileName::LocalPath(absolute) = working_dir {
+                                    // If working_dir has not been remapped, then we emit a
+                                    // LocalPath variant as it's likely to be a valid path
+                                    RealFileName::LocalPath(Path::new(absolute).join(local_path))
+                                } else {
+                                    // If working_dir has been remapped, then we emit
+                                    // Remapped variant as the expanded path won't be valid
+                                    RealFileName::Remapped {
+                                        local_path: None,
+                                        virtual_name: Path::new(working_dir.stable_name())
+                                            .join(local_path),
+                                    }
+                                }
                             }
                             RealFileName::Remapped { local_path: _, virtual_name } => {
-                                FileName::Real(RealFileName::Remapped {
+                                RealFileName::Remapped {
                                     // We do not want any local path to be exported into metadata
                                     local_path: None,
                                     virtual_name: virtual_name.clone(),
-                                })
+                                }
                             }
-                        };
+                        });
                         adapted.name_hash = {
                             let mut hasher: StableHasher = StableHasher::new();
                             adapted.name.hash(&mut hasher);

From ec34cd94dd88d0d564902b5c65d3a2d2e71636c8 Mon Sep 17 00:00:00 2001
From: Andy Wang <cbeuw.andy@gmail.com>
Date: Mon, 19 Apr 2021 20:07:27 +0100
Subject: [PATCH 07/12] Use local name if available in write_out_deps

---
 compiler/rustc_interface/src/passes.rs | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 2f91a72e2081b..1d3109c8a2964 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -36,7 +36,7 @@ use rustc_session::output::{filename_for_input, filename_for_metadata};
 use rustc_session::search_paths::PathKind;
 use rustc_session::Session;
 use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::{FileName, RealFileName};
+use rustc_span::FileName;
 use rustc_trait_selection::traits;
 use rustc_typeck as typeck;
 use tracing::{info, warn};
@@ -532,10 +532,10 @@ fn output_conflicts_with_dir(output_paths: &[PathBuf]) -> Option<PathBuf> {
     check_output(output_paths, check)
 }
 
-fn escape_dep_filename(filename: &FileName) -> String {
+fn escape_dep_filename(filename: &String) -> String {
     // Apparently clang and gcc *only* escape spaces:
     // http://llvm.org/klaus/clang/commit/9d50634cfc268ecc9a7250226dd5ca0e945240d4
-    filename.to_string().replace(" ", "\\ ")
+    filename.replace(" ", "\\ ")
 }
 
 // Makefile comments only need escaping newlines and `\`.
@@ -575,7 +575,14 @@ fn write_out_deps(
             .iter()
             .filter(|fmap| fmap.is_real_file())
             .filter(|fmap| !fmap.is_imported())
-            .map(|fmap| escape_dep_filename(&fmap.name))
+            .map(|fmap| {
+                escape_dep_filename(&match &fmap.name {
+                    FileName::Real(real) => {
+                        real.local_path().unwrap_or(real.stable_name()).display().to_string()
+                    }
+                    _ => fmap.name.to_string(),
+                })
+            })
             .collect();
 
         if let Some(ref backend) = sess.opts.debugging_opts.codegen_backend {
@@ -587,16 +594,13 @@ fn write_out_deps(
                 for cnum in resolver.cstore().crates_untracked() {
                     let source = resolver.cstore().crate_source_untracked(cnum);
                     if let Some((path, _)) = source.dylib {
-                        let file_name = FileName::Real(RealFileName::LocalPath(path));
-                        files.push(escape_dep_filename(&file_name));
+                        files.push(escape_dep_filename(&path.display().to_string()));
                     }
                     if let Some((path, _)) = source.rlib {
-                        let file_name = FileName::Real(RealFileName::LocalPath(path));
-                        files.push(escape_dep_filename(&file_name));
+                        files.push(escape_dep_filename(&path.display().to_string()));
                     }
                     if let Some((path, _)) = source.rmeta {
-                        let file_name = FileName::Real(RealFileName::LocalPath(path));
-                        files.push(escape_dep_filename(&file_name));
+                        files.push(escape_dep_filename(&path.display().to_string()));
                     }
                 }
             });

From f8e55da6defaba1dff4d1e11f76dda3b366819be Mon Sep 17 00:00:00 2001
From: Andy Wang <cbeuw.andy@gmail.com>
Date: Tue, 20 Apr 2021 01:11:12 +0100
Subject: [PATCH 08/12] Remove impl Display for FileName and add
 FileNameDisplay wrapper type

---
 compiler/rustc_span/src/lib.rs                | 51 ++++++++++++++-----
 .../passes/calculate_doc_coverage.rs          |  2 +-
 2 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index e5e6f31886c4b..141ecfd0bafa7 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -202,6 +202,23 @@ impl RealFileName {
             | RealFileName::Remapped { local_path: _, virtual_name: p } => &p,
         }
     }
+
+    fn to_string_lossy(&self, prefer_local: bool) -> Cow<'_, str> {
+        use RealFileName::*;
+        if prefer_local {
+            match self {
+                LocalPath(path)
+                | Remapped { local_path: None, virtual_name: path }
+                | Remapped { local_path: Some(path), virtual_name: _ } => path.to_string_lossy(),
+            }
+        } else {
+            match self {
+                LocalPath(path) | Remapped { local_path: _, virtual_name: path } => {
+                    path.to_string_lossy()
+                }
+            }
+        }
+    }
 }
 
 /// Differentiates between real files and common virtual files.
@@ -228,16 +245,24 @@ pub enum FileName {
     InlineAsm(u64),
 }
 
-impl std::fmt::Display for FileName {
+impl From<PathBuf> for FileName {
+    fn from(p: PathBuf) -> Self {
+        assert!(!p.to_string_lossy().ends_with('>'));
+        FileName::Real(RealFileName::LocalPath(p))
+    }
+}
+
+pub struct FileNameDisplay<'a> {
+    inner: &'a FileName,
+    prefer_local: bool,
+}
+
+impl fmt::Display for FileNameDisplay<'_> {
     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         use FileName::*;
-        match *self {
-            Real(RealFileName::Named(ref path)) => write!(fmt, "{}", path.display()),
-            // FIXME: might be nice to display both components of Devirtualized.
-            // But for now (to backport fix for issue #70924), best to not
-            // perturb diagnostics so its obvious test suite still works.
-            Real(RealFileName::Devirtualized { ref local_path, virtual_name: _ }) => {
-                write!(fmt, "{}", local_path.display())
+        match *self.inner {
+            Real(ref name) => {
+                write!(fmt, "{}", name.to_string_lossy(self.prefer_local))
             }
             QuoteExpansion(_) => write!(fmt, "<quote expansion>"),
             MacroExpansion(_) => write!(fmt, "<macro expansion>"),
@@ -252,10 +277,12 @@ impl std::fmt::Display for FileName {
     }
 }
 
-impl From<PathBuf> for FileName {
-    fn from(p: PathBuf) -> Self {
-        assert!(!p.to_string_lossy().ends_with('>'));
-        FileName::Real(RealFileName::LocalPath(p))
+impl FileNameDisplay<'_> {
+    pub fn to_string_lossy(&self) -> Cow<'_, str> {
+        match self.inner {
+            FileName::Real(ref inner) => inner.to_string_lossy(self.prefer_local),
+            _ => Cow::from(format!("{}", self)),
+        }
     }
 }
 
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index c8b82fb1563df..6c3881811b3d5 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -219,7 +219,7 @@ impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
                 // unless the user had an explicit `allow`
                 let should_have_docs =
                     level != lint::Level::Allow || matches!(source, LintLevelSource::Default);
-                debug!("counting {:?} {:?} in {}", i.type_(), i.name, filename);
+                debug!("counting {:?} {:?} in {:?}", i.type_(), i.name, filename);
                 self.items.entry(filename).or_default().count_item(
                     has_docs,
                     has_doc_example,

From fb4f6439f62d4b940bdfab3f78771d76eacab379 Mon Sep 17 00:00:00 2001
From: Andy Wang <qian.wang19@imperial.ac.uk>
Date: Wed, 14 Apr 2021 14:12:39 +0100
Subject: [PATCH 09/12] Revamp RealFileName public methods

---
 .../src/debuginfo/line_info.rs                |  2 +-
 .../src/debuginfo/mod.rs                      |  2 +-
 .../src/debuginfo/metadata.rs                 |  4 +-
 compiler/rustc_metadata/src/rmeta/encoder.rs  | 35 +++++++++-------
 .../rustc_save_analysis/src/dump_visitor.rs   |  2 +-
 .../rustc_save_analysis/src/span_utils.rs     |  7 +++-
 compiler/rustc_span/src/lib.rs                | 42 ++++++++++---------
 7 files changed, 53 insertions(+), 41 deletions(-)

diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index 8578ab33ced68..b3f700bc46794 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -66,7 +66,7 @@ fn line_program_add_file(
 ) -> FileId {
     match &file.name {
         FileName::Real(path) => {
-            let (dir_path, file_name) = split_path_dir_and_file(path.stable_name());
+            let (dir_path, file_name) = split_path_dir_and_file(path.remapped_path_if_available());
             let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
             let file_name = osstr_as_utf8_bytes(file_name);
 
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
index 10d3651789b3a..61e54a76f29ba 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
@@ -64,7 +64,7 @@ impl<'tcx> DebugContext<'tcx> {
         // FIXME: how to get version when building out of tree?
         // Normally this would use option_env!("CFG_VERSION").
         let producer = format!("cg_clif (rustc {})", "unknown version");
-        let comp_dir = tcx.sess.working_dir.stable_name().to_string_lossy().into_owned();
+        let comp_dir = tcx.sess.working_dir.to_string_lossy(false).into_owned();
         let (name, file_info) = match tcx.sess.local_crate_source_file.clone() {
             Some(path) => {
                 let name = path.to_string_lossy().into_owned();
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index ccef95308500f..6fad1996d7e6f 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -764,7 +764,7 @@ pub fn file_metadata(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll
     let hash = Some(&source_file.src_hash);
     let file_name = Some(source_file.name.to_string());
     let directory = if source_file.is_real_file() && !source_file.is_imported() {
-        Some(cx.sess().working_dir.stable_name().to_string_lossy().to_string())
+        Some(cx.sess().working_dir.to_string_lossy(false).to_string())
     } else {
         // If the path comes from an upstream crate we assume it has been made
         // independent of the compiler's working directory one way or another.
@@ -992,7 +992,7 @@ pub fn compile_unit_metadata(
     let producer = format!("clang LLVM ({})", rustc_producer);
 
     let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
-    let work_dir = tcx.sess.working_dir.stable_name().to_string_lossy();
+    let work_dir = tcx.sess.working_dir.to_string_lossy(false);
     let flags = "\0";
     let out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory;
     let split_name = if tcx.sess.target_can_use_split_dwarf() {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index a3e0dd8e3b9b6..fa62b664b9ecd 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -490,22 +490,27 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     FileName::Real(ref realname) => {
                         let mut adapted = (**source_file).clone();
                         adapted.name = FileName::Real(match realname {
-                            RealFileName::LocalPath(local_path) => {
-                                // Prepend path of working directory onto local path.
-                                // because relative paths are potentially relative to a
-                                // wrong directory.
+                            RealFileName::LocalPath(path_to_file) => {
+                                // Prepend path of working directory onto potentially
+                                // relative paths, because they could become relative
+                                // to a wrong directory.
                                 let working_dir = &self.tcx.sess.working_dir;
-                                if let RealFileName::LocalPath(absolute) = working_dir {
-                                    // If working_dir has not been remapped, then we emit a
-                                    // LocalPath variant as it's likely to be a valid path
-                                    RealFileName::LocalPath(Path::new(absolute).join(local_path))
-                                } else {
-                                    // If working_dir has been remapped, then we emit
-                                    // Remapped variant as the expanded path won't be valid
-                                    RealFileName::Remapped {
-                                        local_path: None,
-                                        virtual_name: Path::new(working_dir.stable_name())
-                                            .join(local_path),
+                                match working_dir {
+                                    RealFileName::LocalPath(absolute) => {
+                                        // If working_dir has not been remapped, then we emit a
+                                        // LocalPath variant as it's likely to be a valid path
+                                        RealFileName::LocalPath(
+                                            Path::new(absolute).join(path_to_file),
+                                        )
+                                    }
+                                    RealFileName::Remapped { local_path: _, virtual_name } => {
+                                        // If working_dir has been remapped, then we emit
+                                        // Remapped variant as the expanded path won't be valid
+                                        RealFileName::Remapped {
+                                            local_path: None,
+                                            virtual_name: Path::new(virtual_name)
+                                                .join(path_to_file),
+                                        }
                                     }
                                 }
                             }
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index ab1b4db6293bd..06b2e41daf5db 100644
--- a/compiler/rustc_save_analysis/src/dump_visitor.rs
+++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -190,7 +190,7 @@ impl<'tcx> DumpVisitor<'tcx> {
         };
 
         let data = CompilationOptions {
-            directory: self.tcx.sess.working_dir.stable_name().into(),
+            directory: self.tcx.sess.working_dir.remapped_path_if_available().into(),
             program,
             arguments,
             output: self.save_ctxt.compilation_output(crate_name),
diff --git a/compiler/rustc_save_analysis/src/span_utils.rs b/compiler/rustc_save_analysis/src/span_utils.rs
index bcda3a6d006f1..3c4d7972380c4 100644
--- a/compiler/rustc_save_analysis/src/span_utils.rs
+++ b/compiler/rustc_save_analysis/src/span_utils.rs
@@ -26,7 +26,12 @@ impl<'a> SpanUtils<'a> {
                         .display()
                         .to_string()
                 } else {
-                    self.sess.working_dir.stable_name().join(&path).display().to_string()
+                    self.sess
+                        .working_dir
+                        .remapped_path_if_available()
+                        .join(&path)
+                        .display()
+                        .to_string()
                 }
             }
             // If the file name was remapped, we assume the user
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 141ecfd0bafa7..787fe1d0f78cf 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -172,7 +172,7 @@ impl<S: Encoder> Encodable<S> for RealFileName {
 impl RealFileName {
     /// Returns the path suitable for reading from the file system on the local host,
     /// if this information exists.
-    /// Avoid embedding this in build artifacts; see `stable_name()` for that.
+    /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that.
     pub fn local_path(&self) -> Option<&Path> {
         match self {
             RealFileName::LocalPath(p) => Some(p),
@@ -184,7 +184,7 @@ impl RealFileName {
 
     /// Returns the path suitable for reading from the file system on the local host,
     /// if this information exists.
-    /// Avoid embedding this in build artifacts; see `stable_name()` for that.
+    /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that.
     pub fn into_local_path(self) -> Option<PathBuf> {
         match self {
             RealFileName::LocalPath(p) => Some(p),
@@ -192,31 +192,33 @@ impl RealFileName {
         }
     }
 
-    /// Returns the path suitable for embedding into build artifacts. Note that
-    /// a remapped path will not correspond to a valid file system path; see
-    /// `local_path()` for something that is more likely to return paths into the
-    /// local host file system.
-    pub fn stable_name(&self) -> &Path {
+    /// Returns the path suitable for embedding into build artifacts. This would still
+    /// be a local path if it has not been remapped. A remapped path will not correspond
+    /// to a valid file system path: see `local_path_if_available()` for something that
+    /// is more likely to return paths into the local host file system.
+    pub fn remapped_path_if_available(&self) -> &Path {
         match self {
             RealFileName::LocalPath(p)
             | RealFileName::Remapped { local_path: _, virtual_name: p } => &p,
         }
     }
 
-    fn to_string_lossy(&self, prefer_local: bool) -> Cow<'_, str> {
-        use RealFileName::*;
+    /// Returns the path suitable for reading from the file system on the local host,
+    /// if this information exists. Otherwise returns the remapped name.
+    /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that.
+    pub fn local_path_if_available(&self) -> &Path {
+        match self {
+            RealFileName::LocalPath(path)
+            | RealFileName::Remapped { local_path: None, virtual_name: path }
+            | RealFileName::Remapped { local_path: Some(path), virtual_name: _ } => path,
+        }
+    }
+
+    pub fn to_string_lossy(&self, prefer_local: bool) -> Cow<'_, str> {
         if prefer_local {
-            match self {
-                LocalPath(path)
-                | Remapped { local_path: None, virtual_name: path }
-                | Remapped { local_path: Some(path), virtual_name: _ } => path.to_string_lossy(),
-            }
+            self.local_path_if_available().to_string_lossy()
         } else {
-            match self {
-                LocalPath(path) | Remapped { local_path: _, virtual_name: path } => {
-                    path.to_string_lossy()
-                }
-            }
+            self.remapped_path_if_available().to_string_lossy()
         }
     }
 }
@@ -1358,7 +1360,7 @@ impl<D: Decoder> Decodable<D> for SourceFile {
 
 impl fmt::Debug for SourceFile {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(fmt, "SourceFile({})", self.name)
+        write!(fmt, "SourceFile({:?})", self.name)
     }
 }
 

From 5417b45c2676dcd396f007bd89ed0cd55d085768 Mon Sep 17 00:00:00 2001
From: Andy Wang <cbeuw.andy@gmail.com>
Date: Mon, 19 Apr 2021 23:27:02 +0100
Subject: [PATCH 10/12] Use local and remapped paths where appropriate

---
 compiler/rustc_builtin_macros/src/source_util.rs |  4 +++-
 compiler/rustc_codegen_cranelift/src/common.rs   |  4 +++-
 .../src/debuginfo/line_info.rs                   |  2 +-
 .../rustc_codegen_llvm/src/debuginfo/metadata.rs |  4 ++--
 compiler/rustc_codegen_ssa/src/mir/block.rs      |  2 +-
 .../src/annotate_snippet_emitter_writer.rs       |  3 ++-
 compiler/rustc_errors/src/emitter.rs             |  8 ++++----
 compiler/rustc_errors/src/json.rs                |  2 +-
 compiler/rustc_expand/src/base.rs                |  5 ++++-
 compiler/rustc_expand/src/expand.rs              |  2 +-
 compiler/rustc_expand/src/proc_macro_server.rs   |  2 +-
 .../rustc_infer/src/infer/error_reporting/mod.rs | 16 +++++++++++-----
 compiler/rustc_interface/src/passes.rs           | 10 +---------
 compiler/rustc_mir/src/interpret/eval_context.rs |  8 +++++++-
 .../src/interpret/intrinsics/caller_location.rs  |  2 +-
 compiler/rustc_mir/src/transform/coverage/mod.rs |  2 +-
 compiler/rustc_parse/src/lib.rs                  |  6 ++++--
 compiler/rustc_save_analysis/src/dump_visitor.rs |  2 +-
 compiler/rustc_save_analysis/src/lib.rs          |  4 ++--
 compiler/rustc_save_analysis/src/span_utils.rs   |  7 +------
 compiler/rustc_span/src/lib.rs                   | 10 ++++++++++
 compiler/rustc_span/src/source_map.rs            |  7 +------
 src/librustdoc/clean/types.rs                    |  2 +-
 src/librustdoc/doctest.rs                        |  7 ++++---
 src/librustdoc/html/render/context.rs            | 15 ++++-----------
 src/librustdoc/html/sources.rs                   | 10 +++++++---
 src/librustdoc/passes/calculate_doc_coverage.rs  |  4 ++--
 src/tools/clippy/clippy_lints/src/macro_use.rs   |  9 ++++-----
 28 files changed, 85 insertions(+), 74 deletions(-)

diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 4aafcb2fb6dfe..ccb9f15b0f505 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -61,7 +61,9 @@ pub fn expand_file(
 
     let topmost = cx.expansion_cause().unwrap_or(sp);
     let loc = cx.source_map().lookup_char_pos(topmost.lo());
-    base::MacEager::expr(cx.expr_str(topmost, Symbol::intern(&loc.file.name.to_string())))
+    base::MacEager::expr(
+        cx.expr_str(topmost, Symbol::intern(&loc.file.name.prefer_remapped().to_string_lossy())),
+    )
 }
 
 pub fn expand_stringify(
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index 92e4435565ee7..c12d6d0f14143 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -334,7 +334,9 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
         let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
         let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
         let const_loc = self.tcx.const_caller_location((
-            rustc_span::symbol::Symbol::intern(&caller.file.name.to_string()),
+            rustc_span::symbol::Symbol::intern(
+                &caller.file.name.prefer_remapped().to_string_lossy(),
+            ),
             caller.line as u32,
             caller.col_display as u32 + 1,
         ));
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index b3f700bc46794..9eb067706309e 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -87,7 +87,7 @@ fn line_program_add_file(
         filename => {
             let dir_id = line_program.default_directory();
             let dummy_file_name = LineString::new(
-                filename.to_string().into_bytes(),
+                filename.prefer_remapped().to_string().into_bytes(),
                 line_program.encoding(),
                 line_strings,
             );
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 6fad1996d7e6f..1eee1eaa211e7 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -759,10 +759,10 @@ fn hex_encode(data: &[u8]) -> String {
 }
 
 pub fn file_metadata(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll DIFile {
-    debug!("file_metadata: file_name: {}", source_file.name);
+    debug!("file_metadata: file_name: {:?}", source_file.name);
 
     let hash = Some(&source_file.src_hash);
-    let file_name = Some(source_file.name.to_string());
+    let file_name = Some(source_file.name.prefer_remapped().to_string());
     let directory = if source_file.is_real_file() && !source_file.is_imported() {
         Some(cx.sess().working_dir.to_string_lossy(false).to_string())
     } else {
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index fd3f89a2aee96..72e9163b88e21 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1144,7 +1144,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
             let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo());
             let const_loc = tcx.const_caller_location((
-                Symbol::intern(&caller.file.name.to_string()),
+                Symbol::intern(&caller.file.name.prefer_remapped().to_string_lossy()),
                 caller.line as u32,
                 caller.col_display as u32 + 1,
             ));
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 6f365c07f6d30..577baec21f064 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -126,7 +126,8 @@ impl AnnotateSnippetEmitterWriter {
             }
             // owned: line source, line index, annotations
             type Owned = (String, usize, Vec<crate::snippet::Annotation>);
-            let origin = primary_lo.file.name.to_string();
+            let filename = primary_lo.file.name.prefer_local();
+            let origin = filename.to_string_lossy();
             let annotated_files: Vec<Owned> = annotated_files
                 .into_iter()
                 .flat_map(|annotated_file| {
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index a58caf2667b06..3443bb5366ca0 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -1309,7 +1309,7 @@ impl EmitterWriter {
                         buffer_msg_line_offset,
                         &format!(
                             "{}:{}:{}",
-                            loc.file.name,
+                            loc.file.name.prefer_local(),
                             sm.doctest_offset_line(&loc.file.name, loc.line),
                             loc.col.0 + 1,
                         ),
@@ -1323,7 +1323,7 @@ impl EmitterWriter {
                         0,
                         &format!(
                             "{}:{}:{}: ",
-                            loc.file.name,
+                            loc.file.name.prefer_local(),
                             sm.doctest_offset_line(&loc.file.name, loc.line),
                             loc.col.0 + 1,
                         ),
@@ -1347,12 +1347,12 @@ impl EmitterWriter {
                     };
                     format!(
                         "{}:{}{}",
-                        annotated_file.file.name,
+                        annotated_file.file.name.prefer_local(),
                         sm.doctest_offset_line(&annotated_file.file.name, first_line.line_index),
                         col
                     )
                 } else {
-                    annotated_file.file.name.to_string()
+                    format!("{}", annotated_file.file.name.prefer_local())
                 };
                 buffer.append(buffer_msg_line_offset + 1, &loc, Style::LineAndColumn);
                 for _ in 0..max_line_num_len {
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 40277006462d2..5d175a3ade9a2 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -468,7 +468,7 @@ impl DiagnosticSpan {
         });
 
         DiagnosticSpan {
-            file_name: start.file.name.to_string(),
+            file_name: start.file.name.prefer_local().to_string(),
             byte_start: start.file.original_relative_byte_pos(span.lo()).0,
             byte_end: start.file.original_relative_byte_pos(span.hi()).0,
             line_start: start.line,
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 5c83d6c7ad598..8a9f3fc668ecf 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1080,7 +1080,10 @@ impl<'a> ExtCtxt<'a> {
                 other => {
                     return Err(self.struct_span_err(
                         span,
-                        &format!("cannot resolve relative path in non-file source `{}`", other),
+                        &format!(
+                            "cannot resolve relative path in non-file source `{}`",
+                            other.prefer_local()
+                        ),
                     ));
                 }
             };
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 03910f4e18d9a..f5c6bb3db6542 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -365,7 +365,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             FileName::Real(name) => name
                 .into_local_path()
                 .expect("attempting to resolve a file path in an external file"),
-            other => PathBuf::from(other.to_string()),
+            other => PathBuf::from(other.prefer_local().to_string()),
         };
         let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
         self.cx.root_path = dir_path.clone();
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index f91c0d8313824..dba708ab2cd9a 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -627,7 +627,7 @@ impl server::SourceFile for Rustc<'_> {
                 .to_str()
                 .expect("non-UTF8 file path in `proc_macro::SourceFile::path`")
                 .to_string(),
-            _ => file.name.to_string(),
+            _ => file.name.prefer_local().to_string(),
         }
     }
     fn is_real(&mut self, file: &Self::SourceFile) -> bool {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index a91bd9ce2ff74..dd3fa248bffea 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1604,13 +1604,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             match (&terr, expected == found) {
                 (TypeError::Sorts(values), extra) => {
                     let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
-                        (true, ty::Opaque(def_id, _)) => format!(
-                            " (opaque type at {})",
-                            self.tcx
+                        (true, ty::Opaque(def_id, _)) => {
+                            let pos = self
+                                .tcx
                                 .sess
                                 .source_map()
-                                .mk_substr_filename(self.tcx.def_span(*def_id)),
-                        ),
+                                .lookup_char_pos(self.tcx.def_span(*def_id).lo());
+                            format!(
+                                " (opaque type at <{}:{}:{}>)",
+                                pos.file.name.prefer_local(),
+                                pos.line,
+                                pos.col.to_usize() + 1,
+                            )
+                        }
                         (true, _) => format!(" ({})", ty.sort_string(self.tcx)),
                         (false, _) => "".to_string(),
                     };
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 1d3109c8a2964..df141f2b7bf27 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -36,7 +36,6 @@ use rustc_session::output::{filename_for_input, filename_for_metadata};
 use rustc_session::search_paths::PathKind;
 use rustc_session::Session;
 use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::FileName;
 use rustc_trait_selection::traits;
 use rustc_typeck as typeck;
 use tracing::{info, warn};
@@ -575,14 +574,7 @@ fn write_out_deps(
             .iter()
             .filter(|fmap| fmap.is_real_file())
             .filter(|fmap| !fmap.is_imported())
-            .map(|fmap| {
-                escape_dep_filename(&match &fmap.name {
-                    FileName::Real(real) => {
-                        real.local_path().unwrap_or(real.stable_name()).display().to_string()
-                    }
-                    _ => fmap.name.to_string(),
-                })
-            })
+            .map(|fmap| escape_dep_filename(&fmap.name.prefer_local().to_string()))
             .collect();
 
         if let Some(ref backend) = sess.opts.debugging_opts.codegen_backend {
diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs
index 2d83d6cfbdc5e..e9dd7a3fe68f1 100644
--- a/compiler/rustc_mir/src/interpret/eval_context.rs
+++ b/compiler/rustc_mir/src/interpret/eval_context.rs
@@ -263,7 +263,13 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> {
             }
             if !self.span.is_dummy() {
                 let lo = tcx.sess.source_map().lookup_char_pos(self.span.lo());
-                write!(f, " at {}:{}:{}", lo.file.name, lo.line, lo.col.to_usize() + 1)?;
+                write!(
+                    f,
+                    " at {}:{}:{}",
+                    lo.file.name.prefer_local(),
+                    lo.line,
+                    lo.col.to_usize() + 1
+                )?;
             }
             Ok(())
         })
diff --git a/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs
index 4dfdc08b875c0..2b996cf62a3d3 100644
--- a/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs
+++ b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs
@@ -106,7 +106,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
         let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
         (
-            Symbol::intern(&caller.file.name.to_string()),
+            Symbol::intern(&caller.file.name.prefer_remapped().to_string_lossy()),
             u32::try_from(caller.line).unwrap(),
             u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(),
         )
diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs
index eaeb44289cfb2..1270be5a52ef4 100644
--- a/compiler/rustc_mir/src/transform/coverage/mod.rs
+++ b/compiler/rustc_mir/src/transform/coverage/mod.rs
@@ -290,7 +290,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         let tcx = self.tcx;
         let source_map = tcx.sess.source_map();
         let body_span = self.body_span;
-        let file_name = Symbol::intern(&self.source_file.name.to_string());
+        let file_name = Symbol::intern(&self.source_file.name.prefer_remapped().to_string_lossy());
 
         let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes());
         for covspan in coverage_spans {
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 905077a48e25a..077b19fa95959 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -188,8 +188,10 @@ pub fn maybe_file_to_stream(
     override_span: Option<Span>,
 ) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> {
     let src = source_file.src.as_ref().unwrap_or_else(|| {
-        sess.span_diagnostic
-            .bug(&format!("cannot lex `source_file` without source: {}", source_file.name));
+        sess.span_diagnostic.bug(&format!(
+            "cannot lex `source_file` without source: {}",
+            source_file.name.prefer_local()
+        ));
     });
 
     let (token_trees, unmatched_braces) =
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index 06b2e41daf5db..54b6a12158581 100644
--- a/compiler/rustc_save_analysis/src/dump_visitor.rs
+++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -1112,7 +1112,7 @@ impl<'tcx> DumpVisitor<'tcx> {
                 name: String::new(),
                 qualname,
                 span,
-                value: filename.to_string(),
+                value: filename.prefer_remapped().to_string(),
                 children,
                 parent: None,
                 decl_id: None,
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs
index c19c16b88a7ab..7e60d881d89fa 100644
--- a/compiler/rustc_save_analysis/src/lib.rs
+++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -80,7 +80,7 @@ impl<'tcx> SaveContext<'tcx> {
         let end = sm.lookup_char_pos(span.hi());
 
         SpanData {
-            file_name: start.file.name.to_string().into(),
+            file_name: start.file.name.prefer_remapped().to_string().into(),
             byte_start: span.lo().0,
             byte_end: span.hi().0,
             line_start: Row::new_one_indexed(start.line as u32),
@@ -290,7 +290,7 @@ impl<'tcx> SaveContext<'tcx> {
                     name: item.ident.to_string(),
                     qualname,
                     span: self.span_from_span(item.ident.span),
-                    value: filename.to_string(),
+                    value: filename.prefer_remapped().to_string(),
                     parent: None,
                     children: m
                         .item_ids
diff --git a/compiler/rustc_save_analysis/src/span_utils.rs b/compiler/rustc_save_analysis/src/span_utils.rs
index 3c4d7972380c4..1947b04f441c3 100644
--- a/compiler/rustc_save_analysis/src/span_utils.rs
+++ b/compiler/rustc_save_analysis/src/span_utils.rs
@@ -34,12 +34,7 @@ impl<'a> SpanUtils<'a> {
                         .to_string()
                 }
             }
-            // If the file name was remapped, we assume the user
-            // configured it the way they wanted to, so use that directly
-            FileName::Real(RealFileName::Remapped { local_path: _, virtual_name }) => {
-                virtual_name.display().to_string()
-            }
-            filename => filename.to_string(),
+            filename => filename.prefer_remapped().to_string(),
         }
     }
 
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 787fe1d0f78cf..458874e4e65f5 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -305,6 +305,16 @@ impl FileName {
         }
     }
 
+    pub fn prefer_remapped(&self) -> FileNameDisplay<'_> {
+        FileNameDisplay { inner: self, prefer_local: false }
+    }
+
+    // This may include transient local filesystem information.
+    // Must not be embedded in build outputs.
+    pub fn prefer_local(&self) -> FileNameDisplay<'_> {
+        FileNameDisplay { inner: self, prefer_local: true }
+    }
+
     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 4e60d071c6873..82e6c579be4fb 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -369,11 +369,6 @@ impl SourceMap {
         source_file
     }
 
-    pub fn mk_substr_filename(&self, sp: Span) -> String {
-        let pos = self.lookup_char_pos(sp.lo());
-        format!("<{}:{}:{}>", pos.file.name, pos.line, pos.col.to_usize() + 1)
-    }
-
     // If there is a doctest offset, applies it to the line.
     pub fn doctest_offset_line(&self, file: &FileName, orig: usize) -> usize {
         match file {
@@ -420,7 +415,7 @@ impl SourceMap {
         let hi = self.lookup_char_pos(sp.hi());
         format!(
             "{}:{}:{}: {}:{}",
-            lo.file.name,
+            lo.file.name.prefer_remapped(),
             lo.line,
             lo.col.to_usize() + 1,
             hi.line,
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 33cb11e539bf2..3999ce7fa2a78 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -93,7 +93,7 @@ impl ExternalCrate {
 
     crate fn src_root(&self, tcx: TyCtxt<'_>) -> PathBuf {
         match self.src(tcx) {
-            FileName::Real(ref p) => match p.local_path().parent() {
+            FileName::Real(ref p) => match p.local_path_if_available().parent() {
                 Some(p) => p.to_path_buf(),
                 None => PathBuf::new(),
             },
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 576f27f8f5c5c..780f451de5963 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -840,7 +840,7 @@ impl Collector {
         if !item_path.is_empty() {
             item_path.push(' ');
         }
-        format!("{} - {}(line {})", filename, item_path, line)
+        format!("{} - {}(line {})", filename.prefer_local(), item_path, line)
     }
 
     crate fn set_position(&mut self, position: Span) {
@@ -891,7 +891,7 @@ impl Tester for Collector {
                     local_path.to_path_buf()
                 } else {
                     // Somehow we got the filename from the metadata of another crate, should never happen
-                    PathBuf::from(r"doctest.rs")
+                    unreachable!("doctest from a different crate");
                 }
             }
             _ => PathBuf::from(r"doctest.rs"),
@@ -899,7 +899,8 @@ impl Tester for Collector {
 
         // For example `module/file.rs` would become `module_file_rs`
         let file = filename
-            .to_string()
+            .prefer_local()
+            .to_string_lossy()
             .chars()
             .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
             .collect::<String>();
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 95f3a32e41f89..288c7785bc9ec 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -376,17 +376,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         } = options;
 
         let src_root = match krate.src {
-            FileName::Real(ref p) => {
-                if let Some(local_path) = p.local_path() {
-                    match local_path.parent() {
-                        Some(p) => p.to_path_buf(),
-                        None => PathBuf::new(),
-                    }
-                } else {
-                    // Somehow we got the filename from the metadata of another crate, should never happen
-                    PathBuf::new()
-                }
-            }
+            FileName::Real(ref p) => match p.local_path_if_available().parent() {
+                Some(p) => p.to_path_buf(),
+                None => PathBuf::new(),
+            },
             _ => PathBuf::new(),
         };
         // If user passed in `--playground-url` arg, we fill in crate name here
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 9753179b6e18d..55bef87496d5e 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -56,7 +56,11 @@ impl DocFolder for SourceCollector<'_, '_> {
                 Err(e) => {
                     self.scx.tcx.sess.span_err(
                         item.span(self.scx.tcx).inner(),
-                        &format!("failed to render source code for `{}`: {}", filename, e),
+                        &format!(
+                            "failed to render source code for `{}`: {}",
+                            filename.prefer_local(),
+                            e
+                        ),
                     );
                     false
                 }
@@ -80,7 +84,7 @@ impl SourceCollector<'_, 'tcx> {
                 if let Some(local_path) = file.local_path() {
                     local_path.to_path_buf()
                 } else {
-                    return Ok(());
+                    unreachable!("only the current crate should have sources emitted");
                 }
             }
             _ => return Ok(()),
@@ -119,7 +123,7 @@ impl SourceCollector<'_, 'tcx> {
         href.push_str(&fname.to_string_lossy());
 
         let title = format!("{} - source", src_fname.to_string_lossy());
-        let desc = format!("Source of the Rust file `{}`.", filename);
+        let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
         let page = layout::Page {
             title: &title,
             css_class: "source",
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 6c3881811b3d5..c115f741afc52 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -119,7 +119,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> {
             &self
                 .items
                 .iter()
-                .map(|(k, v)| (k.to_string(), v))
+                .map(|(k, v)| (k.prefer_local().to_string(), v))
                 .collect::<BTreeMap<String, &ItemCount>>(),
         )
         .expect("failed to convert JSON data to string")
@@ -159,7 +159,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> {
         for (file, &count) in &self.items {
             if let Some(percentage) = count.percentage() {
                 print_table_record(
-                    &limit_filename_len(file.to_string()),
+                    &limit_filename_len(file.prefer_local().to_string_lossy().into()),
                     count,
                     percentage,
                     count.examples_percentage().unwrap_or(0.),
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index ec03daff87b07..314bf11e2d666 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -47,7 +47,7 @@ pub struct MacroRefData {
 
 impl MacroRefData {
     pub fn new(name: String, callee: Span, cx: &LateContext<'_>) -> Self {
-        let mut path = cx.sess().source_map().span_to_filename(callee).to_string();
+        let mut path = cx.sess().source_map().span_to_filename(callee).prefer_local().to_string();
 
         // std lib paths are <::std::module::file type>
         // so remove brackets, space and type.
@@ -96,8 +96,7 @@ impl MacroUseImports {
         let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
         if let Some(callee) = span.source_callee() {
             if !self.collected.contains(&call_site) {
-                self.mac_refs
-                    .push(MacroRefData::new(name.to_string(), callee.def_site, cx));
+                self.mac_refs.push(MacroRefData::new(name.to_string(), callee.def_site, cx));
                 self.collected.insert(call_site);
             }
         }
@@ -175,7 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                                 .push((*item).to_string());
                             check_dup.push((*item).to_string());
                         }
-                    },
+                    }
                     [root, rest @ ..] => {
                         if rest.iter().all(|item| !check_dup.contains(&(*item).to_string())) {
                             let filtered = rest
@@ -199,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                                 .push(rest.join("::"));
                             check_dup.extend(rest.iter().map(ToString::to_string));
                         }
-                    },
+                    }
                 }
             }
         }

From 0ac9ca4f88c03e6fc507ab8937573dca1ccfd2ed Mon Sep 17 00:00:00 2001
From: Andy Wang <cbeuw.andy@gmail.com>
Date: Sat, 1 May 2021 12:28:45 +0100
Subject: [PATCH 11/12] Add -Z simulate-remapped-rust-src-base option to
 simulate path virutalisation during bootstrapping

---
 compiler/rustc_interface/src/tests.rs         |  1 +
 compiler/rustc_metadata/src/rmeta/decoder.rs  | 25 +++++++++++++++++++
 compiler/rustc_session/src/options.rs         |  3 +++
 .../issue-73167-remap-std.rs                  |  9 ++++---
 4 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index d8c1a7a268220..33723e0b51e93 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -595,6 +595,7 @@ fn test_debugging_options_tracking_hash() {
     tracked!(profile_emit, Some(PathBuf::from("abc")));
     tracked!(relax_elf_relocations, Some(true));
     tracked!(relro_level, Some(RelroLevel::Full));
+    tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
     tracked!(report_delayed_bugs, true);
     tracked!(sanitizer, SanitizerSet::ADDRESS);
     tracked!(sanitizer_memory_track_origins, 2);
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 4cb90d5a6d618..493c9dc9563d0 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1707,6 +1707,31 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                         ..
                     } = source_file_to_import;
 
+                    // If this file is under $sysroot/lib/rustlib/src/ but has not been remapped
+                    // during rust bootstrapping by `remap-debuginfo = true`, and the user
+                    // wish to simulate that behaviour by -Z simulate-remapped-rust-src-base,
+                    // then we change `name` to a similar state as if the rust was bootstrapped
+                    // with `remap-debuginfo = true`.
+                    // This is useful for testing so that tests about the effects of
+                    // `try_to_translate_virtual_to_real` don't have to worry about how the
+                    // compiler is bootstrapped.
+                    if let Some(virtual_dir) =
+                        &sess.opts.debugging_opts.simulate_remapped_rust_src_base
+                    {
+                        if let Some(real_dir) = &sess.opts.real_rust_source_base_dir {
+                            if let rustc_span::FileName::Real(ref mut old_name) = name {
+                                if let rustc_span::RealFileName::LocalPath(local) = old_name {
+                                    if let Ok(rest) = local.strip_prefix(real_dir) {
+                                        *old_name = rustc_span::RealFileName::Remapped {
+                                            local_path: None,
+                                            virtual_name: virtual_dir.join(rest),
+                                        };
+                                    }
+                                }
+                            }
+                        }
+                    }
+
                     // If this file's path has been remapped to `/rustc/$hash`,
                     // we might be able to reverse that (also see comments above,
                     // on `try_to_translate_virtual_to_real`).
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 1c2a7f7716d21..3d069aa92e757 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1161,6 +1161,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "whether ELF relocations can be relaxed"),
     relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
         "choose which RELRO level to use"),
+    simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
+        "simulate the effect of remap-debuginfo = true at bootstrapping by remapping path \
+        to rust's source base directory. only meant for testing purposes"),
     report_delayed_bugs: bool = (false, parse_bool, [TRACKED],
         "immediately print bugs registered with `delay_span_bug` (default: no)"),
     sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
diff --git a/src/test/codegen/remap_path_prefix/issue-73167-remap-std.rs b/src/test/codegen/remap_path_prefix/issue-73167-remap-std.rs
index 56c695d4dedcd..b66abc6bedf0f 100644
--- a/src/test/codegen/remap_path_prefix/issue-73167-remap-std.rs
+++ b/src/test/codegen/remap_path_prefix/issue-73167-remap-std.rs
@@ -1,10 +1,13 @@
 // ignore-windows
 
-// compile-flags: -g  -C no-prepopulate-passes --remap-path-prefix=/=/the/root/
+// compile-flags: -g  -C no-prepopulate-passes -Z simulate-remapped-rust-src-base=/rustc/xyz
 
-// Here we check that imported code from std has their path remapped
+// Here we check that importing std will not cause real path to std source files
+// to leak. If rustc was compiled with remap-debuginfo = true, this should be
+// true automatically. If paths to std library hasn't been remapped, we use the
+// above simulate-remapped-rust-src-base option to do it temporarily
 
-// CHECK: !DIFile(filename: "{{/the/root/.*/library/std/src/panic.rs}}"
+// CHECK: !DIFile(filename: "{{/rustc/.*/library/std/src/panic.rs}}"
 fn main() {
     std::thread::spawn(|| {
         println!("hello");

From 37dbe868c92b16211ef2a670f9c9f457515f857b Mon Sep 17 00:00:00 2001
From: Andy Wang <cbeuw.andy@gmail.com>
Date: Mon, 3 May 2021 01:14:25 +0100
Subject: [PATCH 12/12] Split span_to_string into
 span_to_diagnostic/embeddable_string

---
 .../rustc_middle/src/hir/map/collector.rs     |  2 +-
 compiler/rustc_middle/src/mir/mod.rs          |  5 ++++-
 compiler/rustc_middle/src/ty/print/pretty.rs  | 22 ++++++++++++++++---
 .../src/borrow_check/region_infer/dump_mir.rs |  4 +++-
 .../rustc_mir/src/transform/coverage/mod.rs   |  8 +++----
 compiler/rustc_mir/src/util/pretty.rs         | 11 ++++++----
 compiler/rustc_mir/src/util/spanview.rs       |  2 +-
 compiler/rustc_passes/src/liveness.rs         |  6 ++---
 compiler/rustc_passes/src/region.rs           |  2 +-
 compiler/rustc_resolve/src/late/lifetimes.rs  |  2 +-
 compiler/rustc_span/src/lib.rs                |  2 +-
 compiler/rustc_span/src/source_map.rs         | 16 ++++++++++++--
 compiler/rustc_span/src/source_map/tests.rs   |  2 +-
 13 files changed, 60 insertions(+), 24 deletions(-)

diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs
index 501e7d624d2e4..9eac61eb0e59e 100644
--- a/compiler/rustc_middle/src/hir/map/collector.rs
+++ b/compiler/rustc_middle/src/hir/map/collector.rs
@@ -262,7 +262,7 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> {
                     span,
                     "inconsistent DepNode at `{:?}` for `{}`: \
                      current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})",
-                    self.source_map.span_to_string(span),
+                    self.source_map.span_to_diagnostic_string(span),
                     node_str,
                     self.definitions
                         .def_path(self.current_dep_node_owner)
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index e22c0b40d5a53..f83a40d802fee 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -2365,7 +2365,10 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                                 )
                             } else {
                                 let span = tcx.hir().span(hir_id);
-                                format!("[closure@{}]", tcx.sess.source_map().span_to_string(span))
+                                format!(
+                                    "[closure@{}]",
+                                    tcx.sess.source_map().span_to_diagnostic_string(span)
+                                )
                             };
                             let mut struct_fmt = fmt.debug_struct(&name);
 
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 1989c91a87962..5a49dced503b4 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -667,7 +667,12 @@ pub trait PrettyPrinter<'tcx>:
                     if let Some(did) = did.as_local() {
                         let hir_id = self.tcx().hir().local_def_id_to_hir_id(did);
                         let span = self.tcx().hir().span(hir_id);
-                        p!(write("@{}", self.tcx().sess.source_map().span_to_string(span)));
+                        p!(write(
+                            "@{}",
+                            // This may end up in stderr diagnostics but it may also be emitted
+                            // into MIR. Hence we use the remapped path if available
+                            self.tcx().sess.source_map().span_to_embeddable_string(span)
+                        ));
                     } else {
                         p!(write("@"), print_def_path(did, substs));
                     }
@@ -702,7 +707,12 @@ pub trait PrettyPrinter<'tcx>:
                             p!("@", print_def_path(did.to_def_id(), substs));
                         } else {
                             let span = self.tcx().hir().span(hir_id);
-                            p!(write("@{}", self.tcx().sess.source_map().span_to_string(span)));
+                            p!(write(
+                                "@{}",
+                                // This may end up in stderr diagnostics but it may also be emitted
+                                // into MIR. Hence we use the remapped path if available
+                                self.tcx().sess.source_map().span_to_embeddable_string(span)
+                            ));
                         }
                     } else {
                         p!(write("@"), print_def_path(did, substs));
@@ -1407,7 +1417,13 @@ impl<F: fmt::Write> Printer<'tcx> for FmtPrinter<'_, 'tcx, F> {
                 if !self.empty_path {
                     write!(self, "::")?;
                 }
-                write!(self, "<impl at {}>", self.tcx.sess.source_map().span_to_string(span))?;
+                write!(
+                    self,
+                    "<impl at {}>",
+                    // This may end up in stderr diagnostics but it may also be emitted
+                    // into MIR. Hence we use the remapped path if available
+                    self.tcx.sess.source_map().span_to_embeddable_string(span)
+                )?;
                 self.empty_path = false;
 
                 return Ok(self);
diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs b/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs
index 86d9db294bf71..5892ef37ebacb 100644
--- a/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs
+++ b/compiler/rustc_mir/src/borrow_check/region_infer/dump_mir.rs
@@ -76,7 +76,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         for constraint in &constraints {
             let OutlivesConstraint { sup, sub, locations, category } = constraint;
             let (name, arg) = match locations {
-                Locations::All(span) => ("All", tcx.sess.source_map().span_to_string(*span)),
+                Locations::All(span) => {
+                    ("All", tcx.sess.source_map().span_to_embeddable_string(*span))
+                }
                 Locations::Single(loc) => ("Single", format!("{:?}", loc)),
             };
             with_msg(&format!("{:?}: {:?} due to {:?} at {}({})", sup, sub, category, name, arg))?;
diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs
index 1270be5a52ef4..483ceab144233 100644
--- a/compiler/rustc_mir/src/transform/coverage/mod.rs
+++ b/compiler/rustc_mir/src/transform/coverage/mod.rs
@@ -148,8 +148,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         debug!(
             "instrumenting {:?}, fn sig span: {}, body span: {}",
             def_id,
-            source_map.span_to_string(fn_sig_span),
-            source_map.span_to_string(body_span)
+            source_map.span_to_diagnostic_string(fn_sig_span),
+            source_map.span_to_diagnostic_string(body_span)
         );
 
         let mut graphviz_data = debug::GraphvizData::new();
@@ -311,8 +311,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
                 "Calling make_code_region(file_name={}, source_file={:?}, span={}, body_span={})",
                 file_name,
                 self.source_file,
-                source_map.span_to_string(span),
-                source_map.span_to_string(body_span)
+                source_map.span_to_diagnostic_string(span),
+                source_map.span_to_diagnostic_string(body_span)
             );
 
             inject_statement(
diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs
index 3b88aec16b26a..955e85c944a5d 100644
--- a/compiler/rustc_mir/src/util/pretty.rs
+++ b/compiler/rustc_mir/src/util/pretty.rs
@@ -445,7 +445,10 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
             ty::Tuple(tys) if tys.is_empty() => {}
             _ => {
                 self.push("mir::Constant");
-                self.push(&format!("+ span: {}", self.tcx.sess.source_map().span_to_string(*span)));
+                self.push(&format!(
+                    "+ span: {}",
+                    self.tcx.sess.source_map().span_to_embeddable_string(*span)
+                ));
                 if let Some(user_ty) = user_ty {
                     self.push(&format!("+ user_ty: {:?}", user_ty));
                 }
@@ -516,7 +519,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
 }
 
 fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
-    format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_string(span))
+    format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_embeddable_string(span))
 }
 
 /// Prints local variables in a scope tree.
@@ -617,7 +620,7 @@ fn write_scope_tree(
                 "{0:1$} // at {2}",
                 indented_header,
                 ALIGN,
-                tcx.sess.source_map().span_to_string(span),
+                tcx.sess.source_map().span_to_embeddable_string(span),
             )?;
         } else {
             writeln!(w, "{}", indented_header)?;
@@ -1004,7 +1007,7 @@ fn write_user_type_annotations(
             "| {:?}: {:?} at {}",
             index.index(),
             annotation.user_ty,
-            tcx.sess.source_map().span_to_string(annotation.span)
+            tcx.sess.source_map().span_to_embeddable_string(annotation.span)
         )?;
     }
     if !body.user_type_annotations.is_empty() {
diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs
index 9abfa4a8dc68b..2103f2f093407 100644
--- a/compiler/rustc_mir/src/util/spanview.rs
+++ b/compiler/rustc_mir/src/util/spanview.rs
@@ -628,7 +628,7 @@ fn tooltip<'tcx>(
 ) -> String {
     let source_map = tcx.sess.source_map();
     let mut text = Vec::new();
-    text.push(format!("{}: {}:", spanview_id, &source_map.span_to_string(span)));
+    text.push(format!("{}: {}:", spanview_id, &source_map.span_to_embeddable_string(span)));
     for statement in statements {
         let source_range = source_range_no_file(tcx, &statement.source_info.span);
         text.push(format!(
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index fa930471c2110..4ceefa17bcf3d 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -132,9 +132,9 @@ enum LiveNodeKind {
 fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
     let sm = tcx.sess.source_map();
     match lnk {
-        UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_string(s)),
-        ExprNode(s) => format!("Expr node [{}]", sm.span_to_string(s)),
-        VarDefNode(s) => format!("Var def node [{}]", sm.span_to_string(s)),
+        UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_diagnostic_string(s)),
+        ExprNode(s) => format!("Expr node [{}]", sm.span_to_diagnostic_string(s)),
+        VarDefNode(s) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)),
         ClosureNode => "Closure node".to_owned(),
         ExitNode => "Exit node".to_owned(),
     }
diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs
index 14a373c59423f..c133f1a041719 100644
--- a/compiler/rustc_passes/src/region.rs
+++ b/compiler/rustc_passes/src/region.rs
@@ -717,7 +717,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
         debug!(
             "visit_body(id={:?}, span={:?}, body.id={:?}, cx.parent={:?})",
             owner_id,
-            self.tcx.sess.source_map().span_to_string(body.value.span),
+            self.tcx.sess.source_map().span_to_diagnostic_string(body.value.span),
             body_id,
             self.cx.parent
         );
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index 174df09cbdbb2..5919a856ddf14 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -3347,7 +3347,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
     fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
         debug!(
             node = ?self.tcx.hir().node_to_string(lifetime_ref.hir_id),
-            span = ?self.tcx.sess.source_map().span_to_string(lifetime_ref.span)
+            span = ?self.tcx.sess.source_map().span_to_diagnostic_string(lifetime_ref.span)
         );
         self.map.defs.insert(lifetime_ref.hir_id, def);
 
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 458874e4e65f5..0a49e3a430677 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -872,7 +872,7 @@ pub fn debug_with_source_map(
     f: &mut fmt::Formatter<'_>,
     source_map: &SourceMap,
 ) -> fmt::Result {
-    write!(f, "{} ({:?})", source_map.span_to_string(span), span.ctxt())
+    write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(span), span.ctxt())
 }
 
 pub fn default_span_debug(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 82e6c579be4fb..c7ed465df9668 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -406,7 +406,7 @@ impl SourceMap {
         }
     }
 
-    pub fn span_to_string(&self, sp: Span) -> String {
+    fn span_to_string(&self, sp: Span, prefer_local: bool) -> String {
         if self.files.borrow().source_files.is_empty() && sp.is_dummy() {
             return "no-location".to_string();
         }
@@ -415,7 +415,7 @@ impl SourceMap {
         let hi = self.lookup_char_pos(sp.hi());
         format!(
             "{}:{}:{}: {}:{}",
-            lo.file.name.prefer_remapped(),
+            if prefer_local { lo.file.name.prefer_local() } else { lo.file.name.prefer_remapped() },
             lo.line,
             lo.col.to_usize() + 1,
             hi.line,
@@ -423,6 +423,18 @@ impl SourceMap {
         )
     }
 
+    /// Format the span location suitable for embedding in build artifacts
+    pub fn span_to_embeddable_string(&self, sp: Span) -> String {
+        self.span_to_string(sp, false)
+    }
+
+    /// Format the span location to be printed in diagnostics. Must not be emitted
+    /// to build artifacts as this may leak local file paths. Use span_to_embeddable_string
+    /// for string suitable for embedding.
+    pub fn span_to_diagnostic_string(&self, sp: Span) -> String {
+        self.span_to_string(sp, true)
+    }
+
     pub fn span_to_filename(&self, sp: Span) -> FileName {
         self.lookup_char_pos(sp.lo()).file.name.clone()
     }
diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs
index ee87ef0b5e5e7..f13979941abee 100644
--- a/compiler/rustc_span/src/source_map/tests.rs
+++ b/compiler/rustc_span/src/source_map/tests.rs
@@ -193,7 +193,7 @@ fn t8() {
 fn t9() {
     let sm = init_source_map();
     let span = Span::with_root_ctxt(BytePos(12), BytePos(23));
-    let sstr = sm.span_to_string(span);
+    let sstr = sm.span_to_diagnostic_string(span);
 
     assert_eq!(sstr, "blork.rs:2:1: 2:12");
 }