Overhaul filename handling for cross-compiler consistency

This commit refactors `SourceMap` and most importantly `RealFileName` to
make it self-contained in order to achieve cross-compiler consistency.

This is achieved:
 - by making `RealFileName` immutable
 - by only having `SourceMap::to_real_filename` create `RealFileName`
 - by also making `RealFileName` holds it's working directory,
   it's embeddable name and the remapped scopes
 - by making most `FileName` and `RealFileName` methods take a scope as
   an argument

In order for `SourceMap::to_real_filename` to know which scopes to apply
`FilePathMapping` now takes the current remapping scopes to apply, which
makes `FileNameDisplayPreference` and company useless and are removed.

The scopes type `RemapPathScopeComponents` was moved from
`rustc_session::config` to `rustc_span`.

The previous system for scoping the local/remapped filenames
`RemapFileNameExt::for_scope` is no longer useful as it's replaced by
methods on `FileName` and `RealFileName`.
This commit is contained in:
Urgau 2025-11-28 00:01:51 +01:00
parent 5da3de7527
commit 8cbfb26383
32 changed files with 805 additions and 764 deletions

View file

@ -4630,7 +4630,6 @@ dependencies = [
name = "rustc_session"
version = "0.0.0"
dependencies = [
"bitflags",
"getopts",
"libc",
"rand 0.9.2",
@ -4657,6 +4656,7 @@ dependencies = [
name = "rustc_span"
version = "0.0.0"
dependencies = [
"bitflags",
"blake3",
"derive-where",
"indexmap",

View file

@ -68,13 +68,10 @@ pub(crate) fn expand_file(
let topmost = cx.expansion_cause().unwrap_or(sp);
let loc = cx.source_map().lookup_char_pos(topmost.lo());
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::RemapPathScopeComponents;
ExpandResult::Ready(MacEager::expr(cx.expr_str(
topmost,
Symbol::intern(
&loc.file.name.for_scope(cx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
),
Symbol::intern(&loc.file.name.display(RemapPathScopeComponents::MACRO).to_string_lossy()),
)))
}

View file

@ -11,7 +11,7 @@ use rustc_errors::{Applicability, Diag, Level};
use rustc_expand::base::*;
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Ident, Span, Symbol, sym};
use rustc_span::{ErrorGuaranteed, Ident, RemapPathScopeComponents, Span, Symbol, sym};
use thin_vec::{ThinVec, thin_vec};
use tracing::debug;
@ -445,7 +445,7 @@ fn get_location_info(cx: &ExtCtxt<'_>, fn_: &ast::Fn) -> (Symbol, usize, usize,
cx.sess.source_map().span_to_location_info(span);
let file_name = match source_file {
Some(sf) => sf.name.display(FileNameDisplayPreference::Remapped).to_string(),
Some(sf) => sf.name.display(RemapPathScopeComponents::MACRO).to_string(),
None => "no-location".to_string(),
};

View file

@ -21,10 +21,8 @@ use rustc_errors::{DiagCtxtHandle, Level};
use rustc_fs_util::{link_or_copy, path_to_c_string};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_session::config::{
self, Lto, OutputType, Passes, RemapPathScopeComponents, SplitDwarfKind, SwitchWithOptPath,
};
use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym};
use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath};
use rustc_span::{BytePos, InnerSpan, Pos, RemapPathScopeComponents, SpanData, SyntaxContext, sym};
use rustc_target::spec::{
Arch, CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel,
};
@ -248,6 +246,7 @@ pub(crate) fn target_machine_factory(
!sess.opts.unstable_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section);
let path_mapping = sess.source_map().path_mapping().clone();
let working_dir = sess.source_map().working_dir().clone();
let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated);
@ -271,9 +270,6 @@ pub(crate) fn target_machine_factory(
}
};
let file_name_display_preference =
sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO);
let use_wasm_eh = wants_wasm_eh(sess);
let prof = SelfProfilerRef::clone(&sess.prof);
@ -284,8 +280,9 @@ pub(crate) fn target_machine_factory(
let path_to_cstring_helper = |path: Option<PathBuf>| -> CString {
let path = path.unwrap_or_default();
let path = path_mapping
.to_real_filename(path)
.to_string_lossy(file_name_display_preference)
.to_real_filename(&working_dir, path)
.path(RemapPathScopeComponents::DEBUGINFO)
.to_string_lossy()
.into_owned();
CString::new(path).unwrap()
};

View file

@ -7,9 +7,7 @@ use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
use rustc_data_structures::fx::FxIndexMap;
use rustc_index::IndexVec;
use rustc_middle::ty::TyCtxt;
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::{SourceFile, StableSourceFileId};
use rustc_span::{RemapPathScopeComponents, SourceFile, StableSourceFileId};
use tracing::debug;
use crate::common::CodegenCx;
@ -127,10 +125,7 @@ impl GlobalFileTable {
for file in all_files {
raw_file_table.entry(file.stable_id).or_insert_with(|| {
file.name
.for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE)
.to_string_lossy()
.into_owned()
file.name.display(RemapPathScopeComponents::COVERAGE).to_string_lossy().into_owned()
});
}
@ -145,9 +140,10 @@ impl GlobalFileTable {
// resolve any other entries that are stored as relative paths.
let base_dir = tcx
.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::COVERAGE)
.psess
.source_map()
.working_dir()
.path(RemapPathScopeComponents::COVERAGE)
.to_string_lossy();
table.push(base_dir.as_ref());

View file

@ -1,7 +1,7 @@
use std::borrow::Cow;
use std::fmt::{self, Write};
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::sync::Arc;
use std::{iter, ptr};
@ -19,9 +19,7 @@ use rustc_middle::ty::{
self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility,
};
use rustc_session::config::{self, DebugInfo, Lto};
use rustc_span::{
DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Span, Symbol, hygiene,
};
use rustc_span::{DUMMY_SP, FileName, RemapPathScopeComponents, SourceFile, Span, Symbol, hygiene};
use rustc_symbol_mangling::typeid_for_trait_ref;
use rustc_target::spec::DebuginfoKind;
use smallvec::smallvec;
@ -555,79 +553,38 @@ pub(crate) fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFi
) -> &'ll DIFile {
debug!(?source_file.name);
let filename_display_preference =
cx.sess().filename_display_preference(RemapPathScopeComponents::DEBUGINFO);
use rustc_session::config::RemapPathScopeComponents;
let (directory, file_name) = match &source_file.name {
FileName::Real(filename) => {
let working_directory = &cx.sess().opts.working_dir;
debug!(?working_directory);
let (working_directory, embeddable_name) =
filename.embeddable_name(RemapPathScopeComponents::DEBUGINFO);
if filename_display_preference == FileNameDisplayPreference::Remapped {
let filename = cx
.sess()
.source_map()
.path_mapping()
.to_embeddable_absolute_path(filename.clone(), working_directory);
debug!(?working_directory, ?embeddable_name);
// Construct the absolute path of the file
let abs_path = filename.remapped_path_if_available();
debug!(?abs_path);
if let Ok(rel_path) =
abs_path.strip_prefix(working_directory.remapped_path_if_available())
{
// If the compiler's working directory (which also is the DW_AT_comp_dir of
// the compilation unit) is a prefix of the path we are about to emit, then
// only emit the part relative to the working directory. Because of path
// remapping we sometimes see strange things here: `abs_path` might
// actually look like a relative path (e.g.
// `<crate-name-and-version>/src/lib.rs`), so if we emit it without taking
// the working directory into account, downstream tooling will interpret it
// as `<working-directory>/<crate-name-and-version>/src/lib.rs`, which
// makes no sense. Usually in such cases the working directory will also be
// remapped to `<crate-name-and-version>` or some other prefix of the path
// we are remapping, so we end up with
// `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
// By moving the working directory portion into the `directory` part of the
// DIFile, we allow LLVM to emit just the relative path for DWARF, while
// still emitting the correct absolute path for CodeView.
(
working_directory.to_string_lossy(FileNameDisplayPreference::Remapped),
rel_path.to_string_lossy().into_owned(),
)
} else {
("".into(), abs_path.to_string_lossy().into_owned())
}
if let Ok(rel_path) = embeddable_name.strip_prefix(working_directory) {
// If the compiler's working directory (which also is the DW_AT_comp_dir of
// the compilation unit) is a prefix of the path we are about to emit, then
// only emit the part relative to the working directory. Because of path
// remapping we sometimes see strange things here: `abs_path` might
// actually look like a relative path (e.g.
// `<crate-name-and-version>/src/lib.rs`), so if we emit it without taking
// the working directory into account, downstream tooling will interpret it
// as `<working-directory>/<crate-name-and-version>/src/lib.rs`, which
// makes no sense. Usually in such cases the working directory will also be
// remapped to `<crate-name-and-version>` or some other prefix of the path
// we are remapping, so we end up with
// `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
//
// By moving the working directory portion into the `directory` part of the
// DIFile, we allow LLVM to emit just the relative path for DWARF, while
// still emitting the correct absolute path for CodeView.
(working_directory.to_string_lossy(), rel_path.to_string_lossy().into_owned())
} else {
let working_directory = working_directory.local_path_if_available();
let filename = filename.local_path_if_available();
debug!(?working_directory, ?filename);
let abs_path: Cow<'_, Path> = if filename.is_absolute() {
filename.into()
} else {
let mut p = PathBuf::new();
p.push(working_directory);
p.push(filename);
p.into()
};
if let Ok(rel_path) = abs_path.strip_prefix(working_directory) {
(
working_directory.to_string_lossy(),
rel_path.to_string_lossy().into_owned(),
)
} else {
("".into(), abs_path.to_string_lossy().into_owned())
}
("".into(), embeddable_name.to_string_lossy().into_owned())
}
}
other => {
debug!(?other);
("".into(), other.display(filename_display_preference).to_string())
("".into(), other.display(RemapPathScopeComponents::DEBUGINFO).to_string())
}
};
@ -889,12 +846,10 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
codegen_unit_name: &str,
debug_context: &CodegenUnitDebugContext<'ll, 'tcx>,
) -> &'ll DIDescriptor {
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
let mut name_in_debuginfo = tcx
.sess
.local_crate_source_file()
.map(|src| src.for_scope(&tcx.sess, RemapPathScopeComponents::DEBUGINFO).to_path_buf())
.map(|src| src.path(RemapPathScopeComponents::DEBUGINFO).to_path_buf())
.unwrap_or_else(|| PathBuf::from(tcx.crate_name(LOCAL_CRATE).as_str()));
// To avoid breaking split DWARF, we need to ensure that each codegen unit
@ -923,12 +878,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
let producer = format!("clang LLVM ({rustc_producer})");
let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
let work_dir = tcx
.sess
.opts
.working_dir
.for_scope(tcx.sess, RemapPathScopeComponents::DEBUGINFO)
.to_string_lossy();
let work_dir = tcx.sess.psess.source_map().working_dir();
let output_filenames = tcx.output_filenames(());
let split_name = if tcx.sess.target_can_use_split_dwarf()
&& let Some(f) = output_filenames.split_dwarf_path(
@ -938,14 +888,15 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
tcx.sess.invocation_temp.as_deref(),
) {
// We get a path relative to the working directory from split_dwarf_path
Some(tcx.sess.source_map().path_mapping().to_real_filename(f))
Some(tcx.sess.source_map().path_mapping().to_real_filename(work_dir, f))
} else {
None
};
let split_name = split_name
.as_ref()
.map(|f| f.for_scope(tcx.sess, RemapPathScopeComponents::DEBUGINFO).to_string_lossy())
.map(|f| f.path(RemapPathScopeComponents::DEBUGINFO).to_string_lossy())
.unwrap_or_default();
let work_dir = work_dir.path(RemapPathScopeComponents::DEBUGINFO).to_string_lossy();
let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo);
let dwarf_version = tcx.sess.dwarf_version();

View file

@ -208,15 +208,10 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::RemapPathScopeComponents;
(
Symbol::intern(
&caller
.file
.name
.for_scope(self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS)
.to_string_lossy(),
&caller.file.name.display(RemapPathScopeComponents::DIAGNOSTICS).to_string_lossy(),
),
u32::try_from(caller.line).unwrap(),
u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(),

View file

@ -181,7 +181,7 @@ impl<'tcx> pprust_hir::PpAnn for HirTypedAnn<'tcx> {
}
fn get_source(sess: &Session) -> (String, FileName) {
let src_name = sess.io.input.source_name();
let src_name = sess.io.input.file_name(&sess);
let src = String::clone(
sess.source_map()
.get_source_file(&src_name)

View file

@ -11,7 +11,7 @@
use std::error::Report;
use std::io::{self, Write};
use std::path::Path;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::vec;
@ -20,9 +20,9 @@ use derive_setters::Setters;
use rustc_data_structures::sync::IntoDynSyncSend;
use rustc_error_messages::FluentArgs;
use rustc_lint_defs::Applicability;
use rustc_span::Span;
use rustc_span::hygiene::ExpnData;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{FileName, RealFileName, Span};
use serde::Serialize;
use crate::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
@ -490,8 +490,14 @@ impl DiagnosticSpan {
None => {
span = rustc_span::DUMMY_SP;
empty_source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
empty_source_map
.new_source_file(std::path::PathBuf::from("empty.rs").into(), String::new());
empty_source_map.new_source_file(
FileName::Real(
empty_source_map
.path_mapping()
.to_real_filename(&RealFileName::empty(), PathBuf::from("empty.rs")),
),
String::new(),
);
&empty_source_map
}
};

View file

@ -36,11 +36,15 @@ impl<T: Write> Write for Shared<T> {
}
}
fn filename(sm: &SourceMap, path: &str) -> FileName {
FileName::Real(sm.path_mapping().to_real_filename(sm.working_dir(), PathBuf::from(path)))
}
/// Test the span yields correct positions in JSON.
fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
rustc_span::create_default_session_globals_then(|| {
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned());
sm.new_source_file(filename(&sm, "test.rs"), code.to_owned());
let translator =
Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false);

View file

@ -470,7 +470,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.prefer_local().to_string()),
other => PathBuf::from(other.prefer_local_unconditionally().to_string()),
};
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
self.cx.root_path = dir_path.clone();

View file

@ -40,8 +40,7 @@ use rustc_session::output::{collect_crate_types, filename_for_input};
use rustc_session::parse::feature_err;
use rustc_session::search_paths::PathKind;
use rustc_span::{
DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span,
Symbol, sym,
DUMMY_SP, ErrorGuaranteed, ExpnKind, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym,
};
use rustc_trait_selection::{solve, traits};
use tracing::{info, instrument};
@ -595,7 +594,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
.filter(|fmap| !fmap.is_imported())
.map(|fmap| {
(
escape_dep_filename(&fmap.name.prefer_local().to_string()),
escape_dep_filename(&fmap.name.prefer_local_unconditionally().to_string()),
// This needs to be unnormalized,
// as external tools wouldn't know how rustc normalizes them
fmap.unnormalized_source_len as u64,
@ -610,10 +609,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
// (e.g. accessed in proc macros).
let file_depinfo = sess.psess.file_depinfo.borrow();
let normalize_path = |path: PathBuf| {
let file = FileName::from(path);
escape_dep_filename(&file.prefer_local().to_string())
};
let normalize_path = |path: PathBuf| escape_dep_filename(&path.to_string_lossy());
// The entries will be used to declare dependencies between files in a
// Makefile-like output, so the iteration order does not matter.

View file

@ -33,8 +33,8 @@ use rustc_session::config::TargetModifier;
use rustc_session::cstore::{CrateSource, ExternCrate};
use rustc_span::hygiene::HygieneDecodeContext;
use rustc_span::{
BlobDecoder, BytePos, ByteSymbol, DUMMY_SP, Pos, SpanData, SpanDecoder, Symbol, SyntaxContext,
kw,
BlobDecoder, BytePos, ByteSymbol, DUMMY_SP, Pos, RemapPathScopeComponents, SpanData,
SpanDecoder, Symbol, SyntaxContext, kw,
};
use tracing::debug;
@ -1669,15 +1669,15 @@ impl<'a> CrateMetadataRef<'a> {
for virtual_dir in virtual_source_base_dir.iter().flatten() {
if let Some(real_dir) = &real_source_base_dir
&& let rustc_span::FileName::Real(old_name) = name
&& let rustc_span::RealFileName::Remapped { local_path: _, virtual_name } =
old_name
&& let Ok(rest) = virtual_name.strip_prefix(virtual_dir)
&& let (_working_dir, embeddable_name) =
old_name.embeddable_name(RemapPathScopeComponents::MACRO)
&& let Ok(rest) = embeddable_name.strip_prefix(virtual_dir)
{
let new_path = real_dir.join(rest);
debug!(
"try_to_translate_virtual_to_real: `{}` -> `{}`",
virtual_name.display(),
embeddable_name.display(),
new_path.display(),
);
@ -1686,17 +1686,12 @@ impl<'a> CrateMetadataRef<'a> {
// Note that this is a special case for imported rust-src paths specified by
// https://rust-lang.github.io/rfcs/3127-trim-paths.html#handling-sysroot-paths.
// Other imported paths are not currently remapped (see #66251).
let (user_remapped, applied) =
tcx.sess.source_map().path_mapping().map_prefix(&new_path);
let new_name = if applied {
rustc_span::RealFileName::Remapped {
local_path: Some(new_path.clone()),
virtual_name: user_remapped.to_path_buf(),
}
} else {
rustc_span::RealFileName::LocalPath(new_path)
};
*old_name = new_name;
*name = rustc_span::FileName::Real(
tcx.sess
.source_map()
.path_mapping()
.to_real_filename(&rustc_span::RealFileName::empty(), new_path),
);
}
}
};
@ -1711,15 +1706,12 @@ impl<'a> CrateMetadataRef<'a> {
&& let Some(real_dir) = real_source_base_dir
&& let rustc_span::FileName::Real(old_name) = name
{
let relative_path = match old_name {
rustc_span::RealFileName::LocalPath(local) => {
local.strip_prefix(real_dir).ok()
}
rustc_span::RealFileName::Remapped { virtual_name, .. } => {
virtual_source_base_dir
.and_then(|virtual_dir| virtual_name.strip_prefix(virtual_dir).ok())
}
};
let (_working_dir, embeddable_path) =
old_name.embeddable_name(RemapPathScopeComponents::MACRO);
let relative_path = embeddable_path.strip_prefix(real_dir).ok().or_else(|| {
virtual_source_base_dir
.and_then(|virtual_dir| embeddable_path.strip_prefix(virtual_dir).ok())
});
debug!(
?relative_path,
?virtual_dir,
@ -1727,10 +1719,10 @@ impl<'a> CrateMetadataRef<'a> {
"simulate_remapped_rust_src_base"
);
if let Some(rest) = relative_path.and_then(|p| p.strip_prefix(subdir).ok()) {
*old_name = rustc_span::RealFileName::Remapped {
local_path: None,
virtual_name: virtual_dir.join(subdir).join(rest),
};
*name =
rustc_span::FileName::Real(rustc_span::RealFileName::from_virtual_path(
&virtual_dir.join(subdir).join(rest),
))
}
}
};

View file

@ -541,8 +541,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
// is done.
let required_source_files = self.required_source_files.take().unwrap();
let working_directory = &self.tcx.sess.opts.working_dir;
let mut adapted = TableBuilder::default();
let local_crate_stable_id = self.tcx.stable_crate_id(LOCAL_CRATE);
@ -567,10 +565,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
match source_file.name {
FileName::Real(ref original_file_name) => {
let adapted_file_name = source_map
.path_mapping()
.to_embeddable_absolute_path(original_file_name.clone(), working_directory);
let mut adapted_file_name = original_file_name.clone();
adapted_file_name.update_for_crate_metadata();
adapted_source_file.name = FileName::Real(adapted_file_name);
}
_ => {

View file

@ -3,9 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter};
use rustc_abi::{HasDataLayout, Size};
use rustc_hir::def_id::DefId;
use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_session::RemapFileNameExt;
use rustc_session::config::RemapPathScopeComponents;
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_span::{DUMMY_SP, RemapPathScopeComponents, Span, Symbol};
use rustc_type_ir::TypeVisitableExt;
use super::interpret::ReportedErrorInfo;
@ -587,11 +585,7 @@ impl<'tcx> TyCtxt<'tcx> {
let caller = self.sess.source_map().lookup_char_pos(topmost.lo());
self.const_caller_location(
Symbol::intern(
&caller
.file
.name
.for_scope(self.sess, RemapPathScopeComponents::MACRO)
.to_string_lossy(),
&caller.file.name.display(RemapPathScopeComponents::MACRO).to_string_lossy(),
),
caller.line as u32,
caller.col_display as u32 + 1,

View file

@ -16,7 +16,7 @@ use rustc_hir::definitions::{DefKey, DefPathDataName};
use rustc_hir::limit::Limit;
use rustc_macros::{Lift, extension};
use rustc_session::cstore::{ExternCrate, ExternCrateSource};
use rustc_span::{FileNameDisplayPreference, Ident, Symbol, kw, sym};
use rustc_span::{Ident, RemapPathScopeComponents, Symbol, kw, sym};
use rustc_type_ir::{Upcast as _, elaborate};
use smallvec::SmallVec;
@ -890,7 +890,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::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)
self.tcx().sess.source_map().span_to_diagnostic_string(span)
)?;
} else {
write!(self, "@")?;
@ -921,7 +921,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::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)
self.tcx().sess.source_map().span_to_diagnostic_string(span)
)?;
} else {
write!(self, "@")?;
@ -947,10 +947,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
self.print_def_path(did.to_def_id(), args)?;
} else {
let span = self.tcx().def_span(did);
let preference = if with_forced_trimmed_paths() {
FileNameDisplayPreference::Short
let loc = if with_forced_trimmed_paths() {
self.tcx().sess.source_map().span_to_short_string(
span,
RemapPathScopeComponents::DIAGNOSTICS,
)
} else {
FileNameDisplayPreference::Remapped
self.tcx().sess.source_map().span_to_diagnostic_string(span)
};
write!(
self,
@ -958,7 +961,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::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_string(span, preference)
loc
)?;
}
} else {
@ -1004,18 +1007,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
self.print_def_path(did.to_def_id(), args)?;
} else {
let span = self.tcx().def_span(did);
let preference = if with_forced_trimmed_paths() {
FileNameDisplayPreference::Short
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
let loc = if with_forced_trimmed_paths() {
self.tcx().sess.source_map().span_to_short_string(
span,
RemapPathScopeComponents::DIAGNOSTICS,
)
} else {
FileNameDisplayPreference::Remapped
self.tcx().sess.source_map().span_to_diagnostic_string(span)
};
write!(
self,
"@{}",
// 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_string(span, preference)
)?;
write!(self, "@{loc}")?;
}
} else {
write!(self, "@")?;
@ -2258,7 +2260,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
"<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.tcx.sess.source_map().span_to_diagnostic_string(span)
)?;
self.empty_path = false;

View file

@ -129,7 +129,13 @@ pub fn utf8_error<E: EmissionGuarantee>(
note.clone()
};
let contents = String::from_utf8_lossy(contents).to_string();
let source = sm.new_source_file(PathBuf::from(path).into(), contents);
// We only emit this error for files in the current session
// so the working directory can only be the current working directory
let filename = FileName::Real(
sm.path_mapping().to_real_filename(sm.working_dir(), PathBuf::from(path).as_path()),
);
let source = sm.new_source_file(filename, contents);
// Avoid out-of-bounds span from lossy UTF-8 conversion.
if start as u32 > source.normalized_source_len.0 {

View file

@ -2,7 +2,7 @@
use std::assert_matches::assert_matches;
use std::io::prelude::*;
use std::iter::Peekable;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::{io, str};
@ -29,11 +29,15 @@ fn psess() -> ParseSess {
ParseSess::new(vec![crate::DEFAULT_LOCALE_RESOURCE])
}
fn filename(sm: &SourceMap, path: &str) -> FileName {
FileName::Real(sm.path_mapping().to_real_filename(sm.working_dir(), PathBuf::from(path)))
}
/// Map string to parser (via tts).
fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> {
unwrap_or_emit_fatal(new_parser_from_source_str(
psess,
PathBuf::from("bogofile").into(),
filename(psess.source_map(), "bogofile"),
source_str,
StripTokens::Nothing,
))
@ -100,7 +104,7 @@ pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
let psess = psess();
unwrap_or_emit_fatal(source_str_to_stream(
&psess,
PathBuf::from("bogofile").into(),
filename(psess.source_map(), "bogofile"),
source_str,
None,
))
@ -194,8 +198,7 @@ fn test_harness(
(OutputTheme::Unicode, expected_output_unicode),
] {
let (dcx, source_map, output) = create_test_handler(theme);
source_map
.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
source_map.new_source_file(filename(&source_map, "test.rs"), file_text.to_owned());
let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
let mut msp = MultiSpan::from_span(primary_span);
@ -2525,7 +2528,7 @@ fn ttdelim_span() {
create_default_session_globals_then(|| {
let psess = psess();
let expr = parse_expr_from_source_str(
PathBuf::from("foo").into(),
filename(psess.source_map(), "foo"),
"foo!( fn main() { body } )".to_string(),
&psess,
)
@ -2888,10 +2891,11 @@ fn debug_lookahead() {
#[test]
fn out_of_line_mod() {
create_default_session_globals_then(|| {
let psess = psess();
let item = parse_item_from_source_str(
PathBuf::from("foo").into(),
filename(psess.source_map(), "foo"),
"mod foo { struct S; mod this_does_not_exist; }".to_owned(),
&psess(),
&psess,
)
.unwrap()
.unwrap();

View file

@ -6,9 +6,8 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{CRATE_HIR_ID, ItemId, Node, find_attr};
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::RemapFileNameExt;
use rustc_session::config::{CrateType, EntryFnType, RemapPathScopeComponents, sigpipe};
use rustc_span::{Span, sym};
use rustc_session::config::{CrateType, EntryFnType, sigpipe};
use rustc_span::{RemapPathScopeComponents, Span, sym};
use crate::errors::{ExternMain, MultipleRustcMain, NoMainErr};
@ -115,7 +114,7 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
let filename = tcx
.sess
.local_crate_source_file()
.map(|src| src.for_scope(&tcx.sess, RemapPathScopeComponents::DIAGNOSTICS).to_path_buf())
.map(|src| src.path(RemapPathScopeComponents::DIAGNOSTICS).to_path_buf())
.unwrap_or_else(|| {
has_filename = false;
Default::default()

View file

@ -23,7 +23,7 @@ use rustc_middle::ty::{
use rustc_middle::{mir, ty};
use rustc_session::cstore::ForeignModule;
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_span::{FileNameDisplayPreference, Span, Symbol};
use rustc_span::{Span, Symbol};
use rustc_target::callconv::FnAbi;
use super::{AllocRangeHelpers, CompilerCtxt, TyHelpers, TypingEnvHelpers};
@ -324,12 +324,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> {
/// Return filename from given `Span`, for diagnostic purposes.
pub fn get_filename(&self, span: Span) -> String {
self.tcx
.sess
.source_map()
.span_to_filename(span)
.display(FileNameDisplayPreference::Local)
.to_string()
self.tcx.sess.source_map().span_to_filename(span).prefer_local_unconditionally().to_string()
}
/// Return lines corresponding to this `Span`.

View file

@ -2,14 +2,18 @@ use std::path::PathBuf;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::symbol::sym;
use rustc_span::{BytePos, DUMMY_SP, Span};
use rustc_span::{BytePos, DUMMY_SP, FileName, Span};
use super::{DocFragment, DocFragmentKind, source_span_for_markdown_range_inner};
fn filename(sm: &SourceMap, path: &str) -> FileName {
FileName::Real(sm.path_mapping().to_real_filename(sm.working_dir(), PathBuf::from(path)))
}
#[test]
fn single_backtick() {
let sm = SourceMap::new(FilePathMapping::empty());
sm.new_source_file(PathBuf::from("foo.rs").into(), r#"#[doc = "`"] fn foo() {}"#.to_string());
sm.new_source_file(filename(&sm, "foo.rs"), r#"#[doc = "`"] fn foo() {}"#.to_string());
let (span, _) = source_span_for_markdown_range_inner(
&sm,
"`",
@ -32,7 +36,7 @@ fn single_backtick() {
fn utf8() {
// regression test for https://github.com/rust-lang/rust/issues/141665
let sm = SourceMap::new(FilePathMapping::empty());
sm.new_source_file(PathBuf::from("foo.rs").into(), r#"#[doc = "⚠"] fn foo() {}"#.to_string());
sm.new_source_file(filename(&sm, "foo.rs"), r#"#[doc = "⚠"] fn foo() {}"#.to_string());
let (span, _) = source_span_for_markdown_range_inner(
&sm,
"",

View file

@ -5,7 +5,6 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
bitflags = "2.4.1"
getopts = "0.2"
rand = "0.9.0"
rustc_abi = { path = "../rustc_abi" }

View file

@ -24,10 +24,7 @@ use rustc_hashes::Hash64;
use rustc_macros::{BlobDecodable, Decodable, Encodable, HashStable_Generic};
use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION};
use rustc_span::source_map::FilePathMapping;
use rustc_span::{
FileName, FileNameDisplayPreference, FileNameEmbeddablePreference, RealFileName,
SourceFileHashAlgorithm, Symbol, sym,
};
use rustc_span::{FileName, SourceFileHashAlgorithm, Symbol, sym};
use rustc_target::spec::{
FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo,
Target, TargetTuple,
@ -1022,9 +1019,15 @@ impl Input {
"rust_out"
}
pub fn source_name(&self) -> FileName {
pub fn file_name(&self, session: &Session) -> FileName {
match *self {
Input::File(ref ifile) => ifile.clone().into(),
Input::File(ref ifile) => FileName::Real(
session
.psess
.source_map()
.path_mapping()
.to_real_filename(session.psess.source_map().working_dir(), ifile.as_path()),
),
Input::Str { ref name, .. } => name.clone(),
}
}
@ -1312,25 +1315,6 @@ impl OutputFilenames {
}
}
bitflags::bitflags! {
/// Scopes used to determined if it need to apply to --remap-path-prefix
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct RemapPathScopeComponents: u8 {
/// Apply remappings to the expansion of std::file!() macro
const MACRO = 1 << 0;
/// Apply remappings to printed compiler diagnostics
const DIAGNOSTICS = 1 << 1;
/// Apply remappings to debug information
const DEBUGINFO = 1 << 3;
/// Apply remappings to coverage information
const COVERAGE = 1 << 4;
/// An alias for `macro`, `debuginfo` and `coverage`. This ensures all paths in compiled
/// executables, libraries and objects are remapped but not elsewhere.
const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits() | Self::COVERAGE.bits();
}
}
#[derive(Clone, Debug)]
pub struct Sysroot {
pub explicit: Option<PathBuf>,
@ -1369,21 +1353,7 @@ fn file_path_mapping(
remap_path_prefix: Vec<(PathBuf, PathBuf)>,
unstable_opts: &UnstableOptions,
) -> FilePathMapping {
FilePathMapping::new(
remap_path_prefix.clone(),
if unstable_opts.remap_path_scope.contains(RemapPathScopeComponents::DIAGNOSTICS)
&& !remap_path_prefix.is_empty()
{
FileNameDisplayPreference::Remapped
} else {
FileNameDisplayPreference::Local
},
if unstable_opts.remap_path_scope.is_all() {
FileNameEmbeddablePreference::RemappedOnly
} else {
FileNameEmbeddablePreference::LocalAndRemapped
},
)
FilePathMapping::new(remap_path_prefix.clone(), unstable_opts.remap_path_scope)
}
impl Default for Options {
@ -3115,8 +3085,8 @@ pub(crate) mod dep_tracking {
use rustc_errors::LanguageIdentifier;
use rustc_feature::UnstableFeatures;
use rustc_hashes::Hash64;
use rustc_span::RealFileName;
use rustc_span::edition::Edition;
use rustc_span::{RealFileName, RemapPathScopeComponents};
use rustc_target::spec::{
CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel,
RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple,
@ -3128,9 +3098,9 @@ pub(crate) mod dep_tracking {
CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug,
FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto,
LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel,
OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius,
RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind,
SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks,
SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
WasiExecModel,
};
use crate::lint;
use crate::utils::NativeLib;

View file

@ -12,7 +12,7 @@ use rustc_feature::UnstableFeatures;
use rustc_hashes::Hash64;
use rustc_macros::{BlobDecodable, Encodable};
use rustc_span::edition::Edition;
use rustc_span::{RealFileName, SourceFileHashAlgorithm};
use rustc_span::{RemapPathScopeComponents, SourceFileHashAlgorithm};
use rustc_target::spec::{
CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy,
RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility,

View file

@ -1,5 +1,5 @@
use std::any::Any;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
@ -28,7 +28,7 @@ use rustc_macros::HashStable_Generic;
pub use rustc_span::def_id::StableCrateId;
use rustc_span::edition::Edition;
use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol};
use rustc_span::{RealFileName, Span, Symbol};
use rustc_target::asm::InlineAsmArch;
use rustc_target::spec::{
Arch, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
@ -40,8 +40,7 @@ use crate::code_stats::CodeStats;
pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
use crate::config::{
self, CoverageLevel, CoverageOptions, CrateType, DebugInfo, ErrorOutputType, FunctionReturn,
Input, InstrumentCoverage, OptLevel, OutFileName, OutputType, RemapPathScopeComponents,
SwitchWithOptPath,
Input, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath,
};
use crate::filesearch::FileSearch;
use crate::lint::LintId;
@ -192,7 +191,11 @@ impl Session {
}
pub fn local_crate_source_file(&self) -> Option<RealFileName> {
Some(self.source_map().path_mapping().to_real_filename(self.io.input.opt_path()?))
Some(
self.source_map()
.path_mapping()
.to_real_filename(self.source_map().working_dir(), self.io.input.opt_path()?),
)
}
fn check_miri_unleashed_features(&self) -> Option<ErrorGuaranteed> {
@ -846,21 +849,6 @@ impl Session {
self.opts.cg.link_dead_code.unwrap_or(false)
}
pub fn filename_display_preference(
&self,
scope: RemapPathScopeComponents,
) -> FileNameDisplayPreference {
assert!(
scope.bits().count_ones() == 1,
"one and only one scope should be passed to `Session::filename_display_preference`"
);
if self.opts.unstable_opts.remap_path_scope.contains(scope) {
FileNameDisplayPreference::Remapped
} else {
FileNameDisplayPreference::Local
}
}
/// Get the deployment target on Apple platforms based on the standard environment variables,
/// or fall back to the minimum version supported by `rustc`.
///
@ -1496,46 +1484,3 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
};
emitter
}
pub trait RemapFileNameExt {
type Output<'a>
where
Self: 'a;
/// Returns a possibly remapped filename based on the passed scope and remap cli options.
///
/// One and only one scope should be passed to this method, it will panic otherwise.
fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_>;
}
impl RemapFileNameExt for rustc_span::FileName {
type Output<'a> = rustc_span::FileNameDisplay<'a>;
fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_> {
assert!(
scope.bits().count_ones() == 1,
"one and only one scope should be passed to for_scope"
);
if sess.opts.unstable_opts.remap_path_scope.contains(scope) {
self.prefer_remapped_unconditionally()
} else {
self.prefer_local()
}
}
}
impl RemapFileNameExt for rustc_span::RealFileName {
type Output<'a> = &'a Path;
fn for_scope(&self, sess: &Session, scope: RemapPathScopeComponents) -> Self::Output<'_> {
assert!(
scope.bits().count_ones() == 1,
"one and only one scope should be passed to for_scope"
);
if sess.opts.unstable_opts.remap_path_scope.contains(scope) {
self.remapped_path_if_available()
} else {
self.local_path_if_available()
}
}
}

View file

@ -5,6 +5,7 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
bitflags = "2.4.1"
blake3 = "1.5.2"
derive-where = "1.2.7"
indexmap = { version = "2.0.0" }

View file

@ -221,99 +221,227 @@ pub fn with_metavar_spans<R>(f: impl FnOnce(&MetavarSpansMap) -> R) -> R {
with_session_globals(|session_globals| f(&session_globals.metavar_spans))
}
// 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.
bitflags::bitflags! {
/// Scopes used to determined if it need to apply to `--remap-path-prefix`
#[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd, Hash)]
pub struct RemapPathScopeComponents: u8 {
/// Apply remappings to the expansion of `std::file!()` macro
const MACRO = 1 << 0;
/// Apply remappings to printed compiler diagnostics
const DIAGNOSTICS = 1 << 1;
/// Apply remappings to debug information
const DEBUGINFO = 1 << 3;
/// Apply remappings to coverage information
const COVERAGE = 1 << 4;
/// An alias for `macro`, `debuginfo` and `coverage`. This ensures all paths in compiled
/// executables, libraries and objects are remapped but not elsewhere.
const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits() | Self::COVERAGE.bits();
}
}
impl<E: Encoder> Encodable<E> for RemapPathScopeComponents {
fn encode(&self, s: &mut E) {
s.emit_u8(self.bits());
}
}
impl<D: Decoder> Decodable<D> for RemapPathScopeComponents {
fn decode(s: &mut D) -> RemapPathScopeComponents {
RemapPathScopeComponents::from_bits(s.read_u8())
.expect("invalid bits for RemapPathScopeComponents")
}
}
/// A self-contained "real" filename.
///
/// It is produced by `SourceMap::to_real_filename`.
///
/// `RealFileName` represents a filename that may have been (partly) remapped
/// by `--remap-path-prefix` and `-Zremap-path-scope`.
///
/// It also contains an embedabble component which gives a working directory
/// and a maybe-remapped maybe-aboslote name. This is useful for debuginfo where
/// some formats and tools highly prefer absolute paths.
///
/// ## Consistency across compiler sessions
///
/// The type-system, const-eval and other parts of the compiler rely on `FileName`
/// and by extension `RealFileName` to be consistent across compiler sessions.
///
/// Otherwise unsoudness (like rust-lang/rust#148328) may occur.
///
/// As such this type is self-sufficient and consistent in it's output.
///
/// The [`RealFileName::path`] and [`RealFileName::embeddable_name`] methods
/// are guaranteed to always return the same output across compiler sessions.
///
/// ## Usage
///
/// Creation of a [`RealFileName`] should be done using
/// [`FilePathMapping::to_real_filename`][rustc_span::source_map::FilePathMapping::to_real_filename].
///
/// Retrieving a path can be done in two main ways:
/// - by using [`RealFileName::path`] with a given scope (should be preferred)
/// - or by using [`RealFileName::embeddable_name`] with a given scope
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Decodable, Encodable)]
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. 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,
},
pub struct RealFileName {
/// The local name (always present in the original crate)
local: Option<InnerRealFileName>,
/// The maybe remapped part. Correspond to `local` when no remapped happened.
maybe_remapped: InnerRealFileName,
/// The remapped scopes. Any active scope MUST use `maybe_virtual`
scopes: RemapPathScopeComponents,
}
/// The inner workings of `RealFileName`.
///
/// It contains the `name`, `working_directory` and `embeddable_name` components.
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Decodable, Encodable, Hash)]
struct InnerRealFileName {
/// The name.
name: PathBuf,
/// The working directory associated with the embeddable name.
working_directory: PathBuf,
/// The embeddable name.
embeddable_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)
// remapped path if that exists. This is because remapped paths to
// sysroot crates (/rust/$hash or /rust/$version) remain stable even
// if the corresponding local path changes.
if !self.scopes.is_all() {
self.local.hash(state);
}
self.maybe_remapped.hash(state);
self.scopes.bits().hash(state);
}
}
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 `remapped_path_if_available()` for that.
pub fn local_path(&self) -> Option<&Path> {
match self {
RealFileName::LocalPath(p) => Some(p),
RealFileName::Remapped { local_path, virtual_name: _ } => local_path.as_deref(),
}
}
/// 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::LocalPath(p) => Some(p),
RealFileName::Remapped { local_path: p, virtual_name: _ } => p,
}
}
/// 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,
}
}
/// 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,
}
}
/// Return the path remapped or not depending on the [`FileNameDisplayPreference`].
/// Returns the associated path for the given remapping scope.
///
/// For the purpose of this function, local and short preference are equal.
pub fn to_path(&self, display_pref: FileNameDisplayPreference) -> &Path {
match display_pref {
FileNameDisplayPreference::Local | FileNameDisplayPreference::Short => {
self.local_path_if_available()
}
FileNameDisplayPreference::Remapped => self.remapped_path_if_available(),
/// ## Panic
///
/// Only one scope components can be given to this function.
pub fn path(&self, scope: RemapPathScopeComponents) -> &Path {
assert!(
scope.bits().count_ones() == 1,
"one and only one scope should be passed to `RealFileName::path`: {scope:?}"
);
if !self.scopes.contains(scope)
&& let Some(local_name) = &self.local
{
local_name.name.as_path()
} else {
self.maybe_remapped.name.as_path()
}
}
pub fn to_string_lossy(&self, display_pref: FileNameDisplayPreference) -> Cow<'_, str> {
/// Returns the working directory and embeddable path for the given remapping scope.
///
/// Useful for embedding a mostly abosolute path (modulo remapping) in the compiler outputs.
///
/// The embedabble path is not guaranteed to be an absolute path, nor is it garuenteed
/// that the working directory part is always a prefix of embeddable path.
///
/// ## Panic
///
/// Only one scope components can be given to this function.
pub fn embeddable_name(&self, scope: RemapPathScopeComponents) -> (&Path, &Path) {
assert!(
scope.bits().count_ones() == 1,
"one and only one scope should be passed to `RealFileName::embeddable_path`: {scope:?}"
);
if !self.scopes.contains(scope)
&& let Some(local_name) = &self.local
{
(&local_name.working_directory, &local_name.embeddable_name)
} else {
(&self.maybe_remapped.working_directory, &self.maybe_remapped.embeddable_name)
}
}
/// Returns the path suitable for reading from the file system on the local host,
/// if this information exists.
///
/// May not exists if the filename was imported from another crate.
pub fn local_path(&self) -> Option<&Path> {
self.local.as_ref().map(|lp| lp.name.as_ref())
}
/// Returns the path suitable for reading from the file system on the local host,
/// if this information exists.
///
/// May not exists if the filename was imported from another crate.
pub fn into_local_path(self) -> Option<PathBuf> {
self.local.map(|lp| lp.name)
}
/// Returns whenever the filename was remapped.
pub(crate) fn was_remapped(&self) -> bool {
!self.scopes.is_empty()
}
/// Returns an empty `RealFileName`
///
/// Useful as the working directory input to `SourceMap::to_real_filename`.
pub fn empty() -> RealFileName {
RealFileName {
local: Some(InnerRealFileName {
name: PathBuf::new(),
working_directory: PathBuf::new(),
embeddable_name: PathBuf::new(),
}),
maybe_remapped: InnerRealFileName {
name: PathBuf::new(),
working_directory: PathBuf::new(),
embeddable_name: PathBuf::new(),
},
scopes: RemapPathScopeComponents::empty(),
}
}
/// Returns a `RealFileName` that is completely remapped without any local components.
///
/// Only exposed for the purpose of `-Zsimulate-remapped-rust-src-base`.
pub fn from_virtual_path(path: &Path) -> RealFileName {
let name = InnerRealFileName {
name: path.to_owned(),
embeddable_name: path.to_owned(),
working_directory: PathBuf::new(),
};
RealFileName { local: None, maybe_remapped: name, scopes: RemapPathScopeComponents::all() }
}
/// Update the filename for encoding in the crate metadata.
///
/// Currently it's about removing the local part when the filename
/// is fully remapped.
pub fn update_for_crate_metadata(&mut self) {
if self.scopes.is_all() {
self.local = None;
}
}
/// Internal routine to display the filename.
///
/// Users should always use the `RealFileName::path` method or `FileName` methods instead.
fn to_string_lossy<'a>(&'a self, display_pref: FileNameDisplayPreference) -> Cow<'a, str> {
match display_pref {
FileNameDisplayPreference::Local => self.local_path_if_available().to_string_lossy(),
FileNameDisplayPreference::Remapped => {
self.remapped_path_if_available().to_string_lossy()
FileNameDisplayPreference::Remapped => self.maybe_remapped.name.to_string_lossy(),
FileNameDisplayPreference::Local => {
self.local.as_ref().unwrap_or(&self.maybe_remapped).name.to_string_lossy()
}
FileNameDisplayPreference::Short => self
.local_path_if_available()
.maybe_remapped
.name
.file_name()
.map_or_else(|| "".into(), |f| f.to_string_lossy()),
FileNameDisplayPreference::Scope(scope) => self.path(scope).to_string_lossy(),
}
}
}
@ -339,40 +467,20 @@ pub enum FileName {
InlineAsm(Hash64),
}
impl From<PathBuf> for FileName {
fn from(p: PathBuf) -> Self {
FileName::Real(RealFileName::LocalPath(p))
}
}
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub enum FileNameEmbeddablePreference {
/// If a remapped path is available, only embed the `virtual_path` and omit the `local_path`.
///
/// Otherwise embed the local-path into the `virtual_path`.
RemappedOnly,
/// Embed the original path as well as its remapped `virtual_path` component if available.
LocalAndRemapped,
}
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub enum FileNameDisplayPreference {
/// Display the path after the application of rewrite rules provided via `--remap-path-prefix`.
/// This is appropriate for paths that get embedded into files produced by the compiler.
Remapped,
/// Display the path before the application of rewrite rules provided via `--remap-path-prefix`.
/// This is appropriate for use in user-facing output (such as diagnostics).
Local,
/// Display only the filename, as a way to reduce the verbosity of the output.
/// This is appropriate for use in user-facing output (such as diagnostics).
Short,
}
pub struct FileNameDisplay<'a> {
inner: &'a FileName,
display_pref: FileNameDisplayPreference,
}
// Internal enum. Should not be exposed.
#[derive(Clone, Copy)]
enum FileNameDisplayPreference {
Remapped,
Local,
Short,
Scope(RemapPathScopeComponents),
}
impl fmt::Display for FileNameDisplay<'_> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use FileName::*;
@ -417,18 +525,30 @@ impl FileName {
}
}
/// Returns the path suitable for reading from the file system on the local host,
/// if this information exists.
///
/// Avoid embedding this in build artifacts. Prefer using the `display` method.
pub fn prefer_remapped_unconditionally(&self) -> FileNameDisplay<'_> {
FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Remapped }
}
/// This may include transient local filesystem information.
/// Must not be embedded in build outputs.
pub fn prefer_local(&self) -> FileNameDisplay<'_> {
/// Returns the path suitable for reading from the file system on the local host,
/// if this information exists.
///
/// Avoid embedding this in build artifacts. Prefer using the `display` method.
pub fn prefer_local_unconditionally(&self) -> FileNameDisplay<'_> {
FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Local }
}
pub fn display(&self, display_pref: FileNameDisplayPreference) -> FileNameDisplay<'_> {
FileNameDisplay { inner: self, display_pref }
/// Returns a short (either the filename or an empty string).
pub fn short(&self) -> FileNameDisplay<'_> {
FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Short }
}
/// Returns a `Display`-able path for the given scope.
pub fn display(&self, scope: RemapPathScopeComponents) -> FileNameDisplay<'_> {
FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Scope(scope) }
}
pub fn macro_expansion_source_code(src: &str) -> FileName {
@ -473,7 +593,8 @@ impl FileName {
/// 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.
///
/// Avoid embedding this in build artifacts.
pub fn into_local_path(self) -> Option<PathBuf> {
match self {
FileName::Real(path) => path.into_local_path(),

View file

@ -2,6 +2,7 @@ use std::borrow::Borrow;
use rustc_data_structures::profiling::EventArgRecorder;
use crate::RemapPathScopeComponents;
use crate::source_map::SourceMap;
/// Extension trait for self-profiling purposes: allows to record spans within a generic activity's
@ -24,6 +25,6 @@ impl SpannedEventArgRecorder for EventArgRecorder<'_> {
A: Borrow<str> + Into<String>,
{
self.record_arg(event_arg);
self.record_arg(source_map.span_to_embeddable_string(span));
self.record_arg(source_map.span_to_string(span, RemapPathScopeComponents::DEBUGINFO));
}
}

View file

@ -234,7 +234,7 @@ impl SourceMap {
let cwd = file_loader
.current_directory()
.expect("expecting a current working directory to exist");
let working_dir = cwd.to_path_buf().into();
let working_dir = path_mapping.to_real_filename(&RealFileName::empty(), &cwd);
debug!(?working_dir);
SourceMap {
files: Default::default(),
@ -260,7 +260,7 @@ impl SourceMap {
pub fn load_file(&self, path: &Path) -> io::Result<Arc<SourceFile>> {
let src = self.file_loader.read_file(path)?;
let filename = path.to_owned().into();
let filename = FileName::Real(self.path_mapping.to_real_filename(&self.working_dir, path));
Ok(self.new_source_file(filename, src))
}
@ -277,7 +277,8 @@ impl SourceMap {
// via `mod`, so we try to use real file contents and not just an
// empty string.
let text = std::str::from_utf8(&bytes).unwrap_or("").to_string();
let file = self.new_source_file(path.to_owned().into(), text);
let filename = FileName::Real(self.path_mapping.to_real_filename(&self.working_dir, path));
let file = self.new_source_file(filename, text);
Ok((
bytes,
Span::new(
@ -345,7 +346,6 @@ impl SourceMap {
// 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 (filename, _) = self.path_mapping.map_filename_prefix(&filename);
let stable_id = StableSourceFileId::from_filename_in_current_crate(&filename);
match self.source_file_by_stable_id(stable_id) {
@ -444,25 +444,36 @@ impl SourceMap {
}
}
pub fn span_to_string(
pub fn span_to_string(&self, sp: Span, display_scope: RemapPathScopeComponents) -> String {
self.span_to_string_ext(sp, display_scope, false)
}
pub fn span_to_short_string(
&self,
sp: Span,
filename_display_pref: FileNameDisplayPreference,
display_scope: RemapPathScopeComponents,
) -> String {
self.span_to_string_ext(sp, display_scope, true)
}
fn span_to_string_ext(
&self,
sp: Span,
display_scope: RemapPathScopeComponents,
short: bool,
) -> String {
let (source_file, lo_line, lo_col, hi_line, hi_col) = self.span_to_location_info(sp);
let file_name = match source_file {
Some(sf) => sf.name.display(filename_display_pref).to_string(),
Some(sf) => {
if short { sf.name.short() } else { sf.name.display(display_scope) }.to_string()
}
None => return "no-location".to_string(),
};
format!(
"{file_name}:{lo_line}:{lo_col}{}",
if let FileNameDisplayPreference::Short = filename_display_pref {
String::new()
} else {
format!(": {hi_line}:{hi_col}")
}
if short { String::new() } else { format!(": {hi_line}:{hi_col}") }
)
}
@ -479,16 +490,11 @@ impl SourceMap {
(Some(lo.file), lo.line, lo.col.to_usize() + 1, hi.line, hi.col.to_usize() + 1)
}
/// 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, FileNameDisplayPreference::Remapped)
}
/// 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, self.path_mapping.filename_display_for_diagnostics)
self.span_to_string(sp, RemapPathScopeComponents::DIAGNOSTICS)
}
pub fn span_to_filename(&self, sp: Span) -> FileName {
@ -496,7 +502,7 @@ impl SourceMap {
}
pub fn filename_for_diagnostics<'a>(&self, filename: &'a FileName) -> FileNameDisplay<'a> {
filename.display(self.path_mapping.filename_display_for_diagnostics)
filename.display(RemapPathScopeComponents::DIAGNOSTICS)
}
pub fn is_multiline(&self, sp: Span) -> bool {
@ -1045,10 +1051,8 @@ impl SourceMap {
}
pub fn get_source_file(&self, filename: &FileName) -> Option<Arc<SourceFile>> {
// Remap filename before lookup
let filename = self.path_mapping().map_filename_prefix(filename).0;
for sf in self.files.borrow().source_files.iter() {
if filename == sf.name {
if *filename == sf.name {
return Some(Arc::clone(&sf));
}
}
@ -1080,16 +1084,20 @@ impl SourceMap {
return None;
};
let local_path: Cow<'_, Path> = match name {
RealFileName::LocalPath(local_path) => local_path.into(),
RealFileName::Remapped { local_path: Some(local_path), .. } => local_path.into(),
RealFileName::Remapped { local_path: None, virtual_name } => {
let local_path: Cow<'_, Path> = match name.local_path() {
Some(local) => local.into(),
None => {
// The compiler produces better error messages if the sources of dependencies
// are available. Attempt to undo any path mapping so we can find remapped
// dependencies.
//
// We can only use the heuristic because `add_external_src` checks the file
// content hash.
self.path_mapping.reverse_map_prefix_heuristically(virtual_name)?.into()
let maybe_remapped_path = name.path(RemapPathScopeComponents::DIAGNOSTICS);
self.path_mapping
.reverse_map_prefix_heuristically(maybe_remapped_path)
.map(Cow::from)
.unwrap_or(maybe_remapped_path.into())
}
};
@ -1135,35 +1143,25 @@ pub fn get_source_map() -> Option<Arc<SourceMap>> {
#[derive(Clone)]
pub struct FilePathMapping {
mapping: Vec<(PathBuf, PathBuf)>,
filename_display_for_diagnostics: FileNameDisplayPreference,
filename_embeddable_preference: FileNameEmbeddablePreference,
filename_remapping_scopes: RemapPathScopeComponents,
}
impl FilePathMapping {
pub fn empty() -> FilePathMapping {
FilePathMapping::new(
Vec::new(),
FileNameDisplayPreference::Local,
FileNameEmbeddablePreference::RemappedOnly,
)
FilePathMapping::new(Vec::new(), RemapPathScopeComponents::empty())
}
pub fn new(
mapping: Vec<(PathBuf, PathBuf)>,
filename_display_for_diagnostics: FileNameDisplayPreference,
filename_embeddable_preference: FileNameEmbeddablePreference,
filename_remapping_scopes: RemapPathScopeComponents,
) -> FilePathMapping {
FilePathMapping {
mapping,
filename_display_for_diagnostics,
filename_embeddable_preference,
}
FilePathMapping { mapping, filename_remapping_scopes }
}
/// Applies any path prefix substitution as defined by the mapping.
/// The return value is the remapped path and a boolean indicating whether
/// the path was affected by the mapping.
pub fn map_prefix<'a>(&'a self, path: impl Into<Cow<'a, Path>>) -> (Cow<'a, Path>, bool) {
fn map_prefix<'a>(&'a self, path: impl Into<Cow<'a, Path>>) -> (Cow<'a, Path>, bool) {
let path = path.into();
if path.as_os_str().is_empty() {
// Exit early if the path is empty and therefore there's nothing to remap.
@ -1209,138 +1207,68 @@ impl FilePathMapping {
}
}
fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) {
match file {
FileName::Real(realfile) if let RealFileName::LocalPath(local_path) = realfile => {
let (mapped_path, mapped) = self.map_prefix(local_path);
let realfile = if mapped {
RealFileName::Remapped {
local_path: Some(local_path.clone()),
virtual_name: mapped_path.into_owned(),
}
} else {
realfile.clone()
};
(FileName::Real(realfile), mapped)
}
FileName::Real(_) => unreachable!("attempted to remap an already remapped filename"),
other => (other.clone(), false),
}
}
/// Applies any path prefix substitution as defined by the mapping.
/// The return value is the local path with a "virtual path" representing the remapped
/// part if any remapping was performed.
pub fn to_real_filename<'a>(&self, local_path: impl Into<Cow<'a, Path>>) -> RealFileName {
let local_path = local_path.into();
if let (remapped_path, true) = self.map_prefix(&*local_path) {
RealFileName::Remapped {
virtual_name: remapped_path.into_owned(),
local_path: Some(local_path.into_owned()),
}
} else {
RealFileName::LocalPath(local_path.into_owned())
}
}
/// Expand a relative path to an absolute path with remapping taken into account.
/// Use this when absolute paths are required (e.g. debuginfo or crate metadata).
///
/// The resulting `RealFileName` will have its `local_path` portion erased if
/// possible (i.e. if there's also a remapped path).
pub fn to_embeddable_absolute_path(
/// The returned filename contains the a remapped path representing the remapped
/// part if any remapping was performed.
pub fn to_real_filename<'a>(
&self,
file_path: RealFileName,
working_directory: &RealFileName,
local_path: impl Into<Cow<'a, Path>>,
) -> RealFileName {
match file_path {
// Anything that's already remapped we don't modify, except for erasing
// the `local_path` portion (if desired).
RealFileName::Remapped { local_path, virtual_name } => {
RealFileName::Remapped {
local_path: match self.filename_embeddable_preference {
FileNameEmbeddablePreference::RemappedOnly => None,
FileNameEmbeddablePreference::LocalAndRemapped => local_path,
},
// We use the remapped name verbatim, even if it looks like a relative
// path. The assumption is that the user doesn't want us to further
// process paths that have gone through remapping.
virtual_name,
}
}
let local_path = local_path.into();
RealFileName::LocalPath(unmapped_file_path) => {
// If no remapping has been applied yet, try to do so
let (new_path, was_remapped) = self.map_prefix(&unmapped_file_path);
if was_remapped {
// It was remapped, so don't modify further
return RealFileName::Remapped {
virtual_name: new_path.into_owned(),
// But still provide the local path if desired
local_path: match self.filename_embeddable_preference {
FileNameEmbeddablePreference::RemappedOnly => None,
FileNameEmbeddablePreference::LocalAndRemapped => {
Some(unmapped_file_path)
}
},
};
}
let (remapped_path, mut was_remapped) = self.map_prefix(&*local_path);
debug!(?local_path, ?remapped_path, ?was_remapped, ?self.filename_remapping_scopes);
if new_path.is_absolute() {
// No remapping has applied to this path and it is absolute,
// so the working directory cannot influence it either, so
// we are done.
return RealFileName::LocalPath(new_path.into_owned());
}
// Always populate the local part, even if we just remapped it and the scopes are
// total, so that places that load the file from disk still have access to it.
let local = InnerRealFileName {
name: local_path.to_path_buf(),
working_directory: working_directory
.local_path()
.expect("working directory should be local")
.to_path_buf(),
embeddable_name: if local_path.is_absolute() {
local_path.to_path_buf()
} else {
working_directory
.local_path()
.expect("working directory should be local")
.to_path_buf()
.join(&local_path)
},
};
debug_assert!(new_path.is_relative());
let unmapped_file_path_rel = new_path;
RealFileName {
maybe_remapped: InnerRealFileName {
working_directory: working_directory.maybe_remapped.name.clone(),
embeddable_name: if remapped_path.is_absolute() || was_remapped {
// The current directory may have been remapped so we take that
// into account, otherwise we'll forget to include the scopes
was_remapped = was_remapped || working_directory.was_remapped();
match working_directory {
RealFileName::LocalPath(unmapped_working_dir_abs) => {
let unmapped_file_path_abs =
unmapped_working_dir_abs.join(unmapped_file_path_rel);
remapped_path.to_path_buf()
} else {
// Create an absolute path and remap it as well.
let (abs_path, abs_was_remapped) = self.map_prefix(
working_directory.maybe_remapped.name.clone().join(&remapped_path),
);
// Although neither `working_directory` nor the file name were subject
// to path remapping, the concatenation between the two may be. Hence
// we need to do a remapping here.
let (file_path_abs, was_remapped) =
self.map_prefix(&unmapped_file_path_abs);
if was_remapped {
RealFileName::Remapped {
virtual_name: file_path_abs.into_owned(),
local_path: match self.filename_embeddable_preference {
FileNameEmbeddablePreference::RemappedOnly => None,
FileNameEmbeddablePreference::LocalAndRemapped => {
Some(unmapped_file_path_abs)
}
},
}
} else {
// No kind of remapping applied to this path, so
// we leave it as it is.
RealFileName::LocalPath(file_path_abs.into_owned())
}
}
RealFileName::Remapped {
local_path,
virtual_name: remapped_working_dir_abs,
} => {
// If working_directory has been remapped, then we emit
// Remapped variant as the expanded path won't be valid
RealFileName::Remapped {
virtual_name: Path::new(remapped_working_dir_abs)
.join(&unmapped_file_path_rel),
local_path: match self.filename_embeddable_preference {
FileNameEmbeddablePreference::RemappedOnly => None,
FileNameEmbeddablePreference::LocalAndRemapped => local_path
.as_ref()
.map(|local_path| local_path.join(unmapped_file_path_rel)),
},
}
}
}
}
// If either the embeddable name or the working directory was
// remapped, then the filename was remapped
was_remapped = abs_was_remapped || working_directory.was_remapped();
abs_path.to_path_buf()
},
name: remapped_path.to_path_buf(),
},
local: Some(local),
scopes: if was_remapped {
self.filename_remapping_scopes
} else {
RemapPathScopeComponents::empty()
},
}
}

View file

@ -1,10 +1,14 @@
use super::*;
fn filename(sm: &SourceMap, path: &str) -> FileName {
FileName::Real(sm.path_mapping().to_real_filename(sm.working_dir(), PathBuf::from(path)))
}
fn init_source_map() -> SourceMap {
let sm = SourceMap::new(FilePathMapping::empty());
sm.new_source_file(PathBuf::from("blork.rs").into(), "first line.\nsecond line".to_string());
sm.new_source_file(PathBuf::from("empty.rs").into(), String::new());
sm.new_source_file(PathBuf::from("blork2.rs").into(), "first line.\nsecond line".to_string());
sm.new_source_file(filename(&sm, "blork.rs"), "first line.\nsecond line".to_string());
sm.new_source_file(filename(&sm, "empty.rs"), String::new());
sm.new_source_file(filename(&sm, "blork2.rs"), "first line.\nsecond line".to_string());
sm
}
@ -59,15 +63,15 @@ fn t3() {
let sm = init_source_map();
let srcfbp1 = sm.lookup_byte_offset(BytePos(23));
assert_eq!(srcfbp1.sf.name, PathBuf::from("blork.rs").into());
assert_eq!(srcfbp1.sf.name, filename(&sm, "blork.rs"));
assert_eq!(srcfbp1.pos, BytePos(23));
let srcfbp1 = sm.lookup_byte_offset(BytePos(24));
assert_eq!(srcfbp1.sf.name, PathBuf::from("empty.rs").into());
assert_eq!(srcfbp1.sf.name, filename(&sm, "empty.rs"));
assert_eq!(srcfbp1.pos, BytePos(0));
let srcfbp2 = sm.lookup_byte_offset(BytePos(25));
assert_eq!(srcfbp2.sf.name, PathBuf::from("blork2.rs").into());
assert_eq!(srcfbp2.sf.name, filename(&sm, "blork2.rs"));
assert_eq!(srcfbp2.pos, BytePos(0));
}
@ -89,12 +93,12 @@ fn t5() {
let sm = init_source_map();
let loc1 = sm.lookup_char_pos(BytePos(22));
assert_eq!(loc1.file.name, PathBuf::from("blork.rs").into());
assert_eq!(loc1.file.name, filename(&sm, "blork.rs"));
assert_eq!(loc1.line, 2);
assert_eq!(loc1.col, CharPos(10));
let loc2 = sm.lookup_char_pos(BytePos(25));
assert_eq!(loc2.file.name, PathBuf::from("blork2.rs").into());
assert_eq!(loc2.file.name, filename(&sm, "blork2.rs"));
assert_eq!(loc2.line, 1);
assert_eq!(loc2.col, CharPos(0));
}
@ -102,14 +106,8 @@ fn t5() {
fn init_source_map_mbc() -> SourceMap {
let sm = SourceMap::new(FilePathMapping::empty());
// "€" is a three-byte UTF8 char.
sm.new_source_file(
PathBuf::from("blork.rs").into(),
"fir€st €€€€ line.\nsecond line".to_string(),
);
sm.new_source_file(
PathBuf::from("blork2.rs").into(),
"first line€€.\n€ second line".to_string(),
);
sm.new_source_file(filename(&sm, "blork.rs"), "fir€st €€€€ line.\nsecond line".to_string());
sm.new_source_file(filename(&sm, "blork2.rs"), "first line€€.\n€ second line".to_string());
sm
}
@ -138,7 +136,7 @@ fn t7() {
let span = Span::with_root_ctxt(BytePos(12), BytePos(23));
let file_lines = sm.span_to_lines(span).unwrap();
assert_eq!(file_lines.file.name, PathBuf::from("blork.rs").into());
assert_eq!(file_lines.file.name, filename(&sm, "blork.rs"));
assert_eq!(file_lines.lines.len(), 1);
assert_eq!(file_lines.lines[0].line_index, 1);
}
@ -161,7 +159,7 @@ fn span_to_snippet_and_lines_spanning_multiple_lines() {
let sm = SourceMap::new(FilePathMapping::empty());
let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n";
let selection = " \n ~~\n~~~\n~~~~~ \n \n";
sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_string());
sm.new_source_file(filename(&sm, "blork.rs"), inputtext.to_string());
let span = span_from_selection(inputtext, selection);
// Check that we are extracting the text we thought we were extracting.
@ -204,7 +202,7 @@ fn span_merging_fail() {
let inputtext = "bbbb BB\ncc CCC\n";
let selection1 = " ~~\n \n";
let selection2 = " \n ~~~\n";
sm.new_source_file(Path::new("blork.rs").to_owned().into(), inputtext.to_owned());
sm.new_source_file(filename(&sm, "blork.rs"), inputtext.to_owned());
let span1 = span_from_selection(inputtext, selection1);
let span2 = span_from_selection(inputtext, selection2);
@ -218,7 +216,7 @@ fn t10() {
let unnormalized = "first line.\r\nsecond line";
let normalized = "first line.\nsecond line";
let src_file = sm.new_source_file(PathBuf::from("blork.rs").into(), unnormalized.to_string());
let src_file = sm.new_source_file(filename(&sm, "blork.rs"), unnormalized.to_string());
assert_eq!(src_file.src.as_ref().unwrap().as_ref(), normalized);
assert!(
@ -306,8 +304,7 @@ fn path_prefix_remapping() {
{
let mapping = &FilePathMapping::new(
vec![(path("abc/def"), path("foo"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
RemapPathScopeComponents::all(),
);
assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("foo/src/main.rs"));
@ -318,8 +315,7 @@ fn path_prefix_remapping() {
{
let mapping = &FilePathMapping::new(
vec![(path("abc/def"), path("/foo"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
RemapPathScopeComponents::all(),
);
assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("/foo/src/main.rs"));
@ -330,8 +326,7 @@ fn path_prefix_remapping() {
{
let mapping = &FilePathMapping::new(
vec![(path("/abc/def"), path("foo"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
RemapPathScopeComponents::all(),
);
assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("foo/src/main.rs"));
@ -342,8 +337,7 @@ fn path_prefix_remapping() {
{
let mapping = &FilePathMapping::new(
vec![(path("/abc/def"), path("/foo"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
RemapPathScopeComponents::all(),
);
assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("/foo/src/main.rs"));
@ -352,171 +346,322 @@ fn path_prefix_remapping() {
}
#[test]
fn path_prefix_remapping_expand_to_absolute() {
fn to_real_filename_with_full_scopes() {
// "virtual" working directory is relative path
let mapping = &FilePathMapping::new(
vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
RemapPathScopeComponents::all(),
);
let working_directory = path("/foo");
let working_directory = RealFileName::Remapped {
local_path: Some(working_directory.clone()),
virtual_name: mapping.map_prefix(working_directory).0.into_owned(),
};
let working_directory = mapping.to_real_filename(&RealFileName::empty(), working_directory);
assert_eq!(working_directory.remapped_path_if_available(), path("FOO"));
assert_eq!(working_directory.path(RemapPathScopeComponents::DIAGNOSTICS), path("FOO"));
assert_eq!(working_directory.path(RemapPathScopeComponents::MACRO), path("FOO"));
assert_eq!(working_directory.path(RemapPathScopeComponents::DEBUGINFO), path("FOO"));
// Unmapped absolute path
// Absolute path
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("/foo/src/main.rs")),
&working_directory
),
RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") }
);
// Unmapped absolute path with unrelated working directory
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("/bar/src/main.rs")),
&working_directory
),
RealFileName::Remapped { local_path: None, virtual_name: path("BAR/src/main.rs") }
);
// Unmapped absolute path that does not match any prefix
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("/quux/src/main.rs")),
&working_directory
),
RealFileName::LocalPath(path("/quux/src/main.rs")),
);
// Unmapped relative path
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("src/main.rs")),
&working_directory
),
RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") }
);
// Unmapped relative path with `./`
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("./src/main.rs")),
&working_directory
),
RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") }
);
// Unmapped relative path that does not match any prefix
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("quux/src/main.rs")),
&RealFileName::LocalPath(path("/abc")),
),
RealFileName::LocalPath(path("/abc/quux/src/main.rs")),
);
// Already remapped absolute path
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::Remapped {
local_path: Some(path("/foo/src/main.rs")),
virtual_name: path("FOO/src/main.rs"),
mapping.to_real_filename(&working_directory, path("/foo/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/foo/src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/foo/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("FOO/src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("FOO/src/main.rs")
},
&working_directory
),
RealFileName::Remapped { local_path: None, virtual_name: path("FOO/src/main.rs") }
scopes: RemapPathScopeComponents::all()
}
);
// Already remapped absolute path, with unrelated working directory
// Absolute path with unrelated working directory
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::Remapped {
local_path: Some(path("/bar/src/main.rs")),
virtual_name: path("BAR/src/main.rs"),
mapping.to_real_filename(&working_directory, path("/bar/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/bar/src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/bar/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("BAR/src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("BAR/src/main.rs")
},
&working_directory
),
RealFileName::Remapped { local_path: None, virtual_name: path("BAR/src/main.rs") }
scopes: RemapPathScopeComponents::all()
}
);
// Already remapped relative path
// Absolute path that does not match any prefix
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") },
&working_directory
mapping.to_real_filename(&working_directory, path("/quux/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/quux/src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/quux/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("/quux/src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("/quux/src/main.rs")
},
scopes: RemapPathScopeComponents::all()
}
);
// Relative path
assert_eq!(
mapping.to_real_filename(&working_directory, path("src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/foo/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("FOO/src/main.rs")
},
scopes: RemapPathScopeComponents::all()
}
);
// Relative path with `./`
assert_eq!(
mapping.to_real_filename(&working_directory, path("./src/main.rs"),),
RealFileName {
local: Some(InnerRealFileName {
name: path("./src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/foo/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("./src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("FOO/src/main.rs")
},
scopes: RemapPathScopeComponents::all()
}
);
// Relative path that does not match any prefix
assert_eq!(
mapping.to_real_filename(
&mapping.to_real_filename(&RealFileName::empty(), path("/abc")),
path("quux/src/main.rs"),
),
RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") }
RealFileName {
local: Some(InnerRealFileName {
name: path("quux/src/main.rs"),
working_directory: path("/abc"),
embeddable_name: path("/abc/quux/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("quux/src/main.rs"),
working_directory: path("/abc"),
embeddable_name: path("/abc/quux/src/main.rs")
},
scopes: RemapPathScopeComponents::empty()
}
);
}
#[test]
fn path_prefix_remapping_expand_to_absolute_and_local() {
fn to_real_filename_with_mixed_scopes() {
// "virtual" working directory is relative path
let mapping = &FilePathMapping::new(
vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::LocalAndRemapped,
RemapPathScopeComponents::OBJECT,
);
let working_directory = path("/foo");
let working_directory = RealFileName::Remapped {
local_path: Some(working_directory.clone()),
virtual_name: mapping.map_prefix(working_directory).0.into_owned(),
};
let working_directory = mapping.to_real_filename(&RealFileName::empty(), working_directory);
assert_eq!(working_directory.remapped_path_if_available(), path("FOO"));
assert_eq!(working_directory.path(RemapPathScopeComponents::DIAGNOSTICS), path("/foo"));
assert_eq!(working_directory.path(RemapPathScopeComponents::MACRO), path("FOO"));
assert_eq!(working_directory.path(RemapPathScopeComponents::DEBUGINFO), path("FOO"));
// Unmapped absolute path
// Absolute path
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("/foo/src/main.rs")),
&working_directory
),
RealFileName::Remapped {
local_path: Some(path("/foo/src/main.rs")),
virtual_name: path("FOO/src/main.rs")
}
);
// Unmapped absolute path with unrelated working directory
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::LocalPath(path("/bar/src/main.rs")),
&working_directory
),
RealFileName::Remapped {
local_path: Some(path("/bar/src/main.rs")),
virtual_name: path("BAR/src/main.rs")
}
);
// Already remapped absolute path, with unrelated working directory
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::Remapped {
local_path: Some(path("/bar/src/main.rs")),
virtual_name: path("BAR/src/main.rs"),
mapping.to_real_filename(&working_directory, path("/foo/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/foo/src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/foo/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("FOO/src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("FOO/src/main.rs")
},
&working_directory
),
RealFileName::Remapped {
local_path: Some(path("/bar/src/main.rs")),
virtual_name: path("BAR/src/main.rs")
scopes: RemapPathScopeComponents::OBJECT
}
);
// Already remapped relative path
// Absolute path with unrelated working directory
assert_eq!(
mapping.to_embeddable_absolute_path(
RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") },
&working_directory
mapping.to_real_filename(&working_directory, path("/bar/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/bar/src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/bar/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("BAR/src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("BAR/src/main.rs")
},
scopes: RemapPathScopeComponents::OBJECT
}
);
// Absolute path without remapping
assert_eq!(
mapping.to_real_filename(&working_directory, path("/quux/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/quux/src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/quux/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("/quux/src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("/quux/src/main.rs")
},
scopes: RemapPathScopeComponents::OBJECT
}
);
// Relative path
assert_eq!(
mapping.to_real_filename(&working_directory, path("src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("src/main.rs"),
working_directory: path("/foo"),
embeddable_name: path("/foo/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("src/main.rs"),
working_directory: path("FOO"),
embeddable_name: path("FOO/src/main.rs")
},
scopes: RemapPathScopeComponents::OBJECT
}
);
// Relative path that does not match any prefix
assert_eq!(
mapping.to_real_filename(
&mapping.to_real_filename(&RealFileName::empty(), path("/abc")),
path("quux/src/main.rs"),
),
RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") }
RealFileName {
local: Some(InnerRealFileName {
name: path("quux/src/main.rs"),
working_directory: path("/abc"),
embeddable_name: path("/abc/quux/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("quux/src/main.rs"),
working_directory: path("/abc"),
embeddable_name: path("/abc/quux/src/main.rs")
},
scopes: RemapPathScopeComponents::empty()
}
);
}
#[test]
fn to_real_filename_without_remapped_cwd() {
// "virtual" working directory is relative path
let mapping = &FilePathMapping::new(
vec![(path("/foo"), path("FOO")), (path("/cwd/bar"), path("CWDBAR"))],
RemapPathScopeComponents::OBJECT,
);
let working_directory = path("/cwd");
let working_directory = mapping.to_real_filename(&RealFileName::empty(), working_directory);
assert_eq!(working_directory.path(RemapPathScopeComponents::DIAGNOSTICS), path("/cwd"));
assert_eq!(working_directory.path(RemapPathScopeComponents::MACRO), path("/cwd"));
assert_eq!(working_directory.path(RemapPathScopeComponents::DEBUGINFO), path("/cwd"));
// Absolute path
assert_eq!(
mapping.to_real_filename(&working_directory, path("/foo/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/foo/src/main.rs"),
working_directory: path("/cwd"),
embeddable_name: path("/foo/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("FOO/src/main.rs"),
working_directory: path("/cwd"),
embeddable_name: path("FOO/src/main.rs")
},
scopes: RemapPathScopeComponents::OBJECT
}
);
// Absolute path with unrelated root
assert_eq!(
mapping.to_real_filename(&working_directory, path("/bar/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/bar/src/main.rs"),
working_directory: path("/cwd"),
embeddable_name: path("/bar/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("/bar/src/main.rs"),
working_directory: path("/cwd"),
embeddable_name: path("/bar/src/main.rs")
},
scopes: RemapPathScopeComponents::empty()
}
);
// Absolute path with cwd
assert_eq!(
mapping.to_real_filename(&working_directory, path("/cwd/bar/src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("/cwd/bar/src/main.rs"),
working_directory: path("/cwd"),
embeddable_name: path("/cwd/bar/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("CWDBAR/src/main.rs"),
working_directory: path("/cwd"),
embeddable_name: path("CWDBAR/src/main.rs")
},
scopes: RemapPathScopeComponents::OBJECT
}
);
// Relative path
assert_eq!(
mapping.to_real_filename(&working_directory, path("src/main.rs")),
RealFileName {
local: Some(InnerRealFileName {
name: path("src/main.rs"),
working_directory: path("/cwd"),
embeddable_name: path("/cwd/src/main.rs")
}),
maybe_remapped: InnerRealFileName {
name: path("src/main.rs"),
working_directory: path("/cwd"),
embeddable_name: path("/cwd/src/main.rs")
},
scopes: RemapPathScopeComponents::empty()
}
);
}
@ -526,8 +671,7 @@ fn path_prefix_remapping_reverse() {
{
let mapping = &FilePathMapping::new(
vec![(path("abc"), path("/")), (path("def"), path("."))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
RemapPathScopeComponents::all(),
);
assert_eq!(reverse_map_prefix(mapping, "/hello.rs"), None);
@ -538,8 +682,7 @@ fn path_prefix_remapping_reverse() {
{
let mapping = &FilePathMapping::new(
vec![(path("abc"), path("/redacted")), (path("def"), path("/redacted"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
RemapPathScopeComponents::all(),
);
assert_eq!(reverse_map_prefix(mapping, "/redacted/hello.rs"), None);
@ -549,8 +692,7 @@ fn path_prefix_remapping_reverse() {
{
let mapping = &FilePathMapping::new(
vec![(path("abc"), path("/redacted")), (path("def/ghi"), path("/fake/dir"))],
FileNameDisplayPreference::Remapped,
FileNameEmbeddablePreference::RemappedOnly,
RemapPathScopeComponents::all(),
);
assert_eq!(
@ -567,7 +709,7 @@ fn path_prefix_remapping_reverse() {
#[test]
fn test_next_point() {
let sm = SourceMap::new(FilePathMapping::empty());
sm.new_source_file(PathBuf::from("example.rs").into(), "a…b".to_string());
sm.new_source_file(filename(&sm, "example.rs"), "a…b".to_string());
// Dummy spans don't advance.
let span = DUMMY_SP;

View file

@ -11,7 +11,7 @@ fn rustc_with_common_args() -> Rustc {
fn main() {
rustc_with_common_args()
.input("foo-v1.rs")
.input(cwd().join("foo-v1.rs"))
.crate_type("rlib")
.crate_name("foo")
.extra_filename("-v1")
@ -19,7 +19,7 @@ fn main() {
.run();
rustc_with_common_args()
.input("foo-v2.rs")
.input(cwd().join("foo-v2.rs"))
.crate_type("rlib")
.crate_name("foo")
.extra_filename("-v2")
@ -27,7 +27,7 @@ fn main() {
.run();
rustc_with_common_args()
.input("re-export-foo.rs")
.input(cwd().join("re-export-foo.rs"))
.crate_type("rlib")
.extern_("foo", rust_lib_name("foo-v2"))
.run();

View file

@ -10,7 +10,7 @@ help: the trait `std::fmt::Display` is not implemented for `A`
LL | struct A;
| ^^^^^^^^
note: required by a bound in `Trait`
--> $DIR/auxiliary/trait-diag.rs:LL:COL
--> remapped/errors/auxiliary/trait-diag.rs:LL:COL
|
LL | pub trait Trait: std::fmt::Display {}
| ^^^^^^^^^^^^^^^^^ required by this bound in `Trait`