diff options
| author | bors <bors@rust-lang.org> | 2021-05-12 11:05:56 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2021-05-12 11:05:56 +0000 |
| commit | e1ff91f439bc09f566da211c6449821b4e949279 (patch) | |
| tree | caf30c76d01ab56c1307a6a10d8f73cd8b925b7c /compiler/rustc_span/src | |
| parent | ac923d94f86a6f7c881ecbedcd0a68d7986a35bd (diff) | |
| parent | 37dbe868c92b16211ef2a670f9c9f457515f857b (diff) | |
| download | rust-e1ff91f439bc09f566da211c6449821b4e949279.tar.gz rust-e1ff91f439bc09f566da211c6449821b4e949279.zip | |
Auto merge of #83813 - cbeuw:remap-std, r=michaelwoerister
Fix `--remap-path-prefix` not correctly remapping `rust-src` component paths and unify handling of path mapping with virtualized paths
This PR fixes #73167 ("Binaries end up containing path to the rust-src component despite `--remap-path-prefix`") by preventing real local filesystem paths from reaching compilation output if the path is supposed to be remapped.
`RealFileName::Named` introduced in #72767 is now renamed as `LocalPath`, because this variant wraps a (most likely) valid local filesystem path.
`RealFileName::Devirtualized` is renamed as `Remapped` to be used for remapped path from a real path via `--remap-path-prefix` argument, as well as real path inferred from a virtualized (during compiler bootstrapping) `/rustc/...` path. The `local_path` field is now an `Option<PathBuf>`, as it will be set to `None` before serialisation, so it never reaches any build output. Attempting to serialise a non-`None` `local_path` will cause an assertion faliure.
When a path is remapped, a `RealFileName::Remapped` variant is created. The original path is preserved in `local_path` field and the remapped path is saved in `virtual_name` field. Previously, the `local_path` is directly modified which goes against its purpose of "suitable for reading from the file system on the local host".
`rustc_span::SourceFile`'s fields `unmapped_path` (introduced by #44940) and `name_was_remapped` (introduced by #41508 when `--remap-path-prefix` feature originally added) are removed, as these two pieces of information can be inferred from the `name` field: if it's anything other than a `FileName::Real(_)`, or if it is a `FileName::Real(RealFileName::LocalPath(_))`, then clearly `name_was_remapped` would've been false and `unmapped_path` would've been `None`. If it is a `FileName::Real(RealFileName::Remapped{local_path, virtual_name})`, then `name_was_remapped` would've been true and `unmapped_path` would've been `Some(local_path)`.
cc `@eddyb` who implemented `/rustc/...` path devirtualisation
Diffstat (limited to 'compiler/rustc_span/src')
| -rw-r--r-- | compiler/rustc_span/src/lib.rs | 176 | ||||
| -rw-r--r-- | compiler/rustc_span/src/source_map.rs | 112 | ||||
| -rw-r--r-- | compiler/rustc_span/src/source_map/tests.rs | 4 |
3 files changed, 165 insertions, 127 deletions
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index d56b434ca85..0301b364591 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -114,52 +114,112 @@ 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)] +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd)] +#[derive(HashStable_Generic, Decodable)] 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 { - /// `local_path` is the (host-dependent) local path to the file. - local_path: PathBuf, + 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. 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. - /// Avoid embedding this in build artifacts; see `stable_name()` for that. - pub fn local_path(&self) -> &Path { + /// 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 `remapped_path_if_available()` for that. + pub fn local_path(&self) -> Option<&Path> { match self { - RealFileName::Named(p) - | RealFileName::Devirtualized { 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. - /// Avoid embedding this in build artifacts; see `stable_name()` for that. - pub fn into_local_path(self) -> PathBuf { + /// 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 `remapped_path_if_available()` for that. + pub fn into_local_path(self) -> Option<PathBuf> { match self { - RealFileName::Named(p) - | RealFileName::Devirtualized { local_path: p, virtual_name: _ } => p, + RealFileName::LocalPath(p) => Some(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 - /// `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::Named(p) - | RealFileName::Devirtualized { local_path: _, virtual_name: p } => &p, + RealFileName::LocalPath(p) + | RealFileName::Remapped { local_path: _, virtual_name: p } => &p, + } + } + + /// 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 { + self.local_path_if_available().to_string_lossy() + } else { + self.remapped_path_if_available().to_string_lossy() } } } @@ -188,16 +248,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>"), @@ -212,10 +280,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::Named(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)), + } } } @@ -236,6 +306,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); @@ -796,7 +876,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 { @@ -1128,11 +1208,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. @@ -1162,7 +1237,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))?; @@ -1237,8 +1311,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 = @@ -1282,8 +1354,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, @@ -1304,15 +1374,13 @@ 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) } } impl SourceFile { pub fn new( name: FileName, - name_was_remapped: bool, - unmapped_path: FileName, mut src: String, start_pos: BytePos, hash_kind: SourceFileHashAlgorithm, @@ -1334,8 +1402,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 e365844980b..0dadd419200 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,13 @@ 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); + name.hash(&mut hasher); StableSourceFileId(hasher.finish()) } @@ -283,35 +266,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 +283,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 +306,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 +342,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 { @@ -411,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 { @@ -453,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(); } @@ -462,7 +415,7 @@ impl SourceMap { let hi = self.lookup_char_pos(sp.hi()); format!( "{}:{}:{}: {}:{}", - lo.file.name, + if prefer_local { lo.file.name.prefer_local() } else { lo.file.name.prefer_remapped() }, lo.line, lo.col.to_usize() + 1, hi.line, @@ -470,16 +423,20 @@ impl SourceMap { ) } - pub fn span_to_filename(&self, sp: Span) -> FileName { - self.lookup_char_pos(sp.lo()).file.name.clone() + /// 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_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 span_to_filename(&self, sp: Span) -> FileName { + self.lookup_char_pos(sp.lo()).file.name.clone() } pub fn is_multiline(&self, sp: Span) -> bool { @@ -1001,7 +958,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, }) } @@ -1046,9 +1009,20 @@ 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 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(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 7d814f1d82c..f13979941ab 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"); } @@ -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(), |
