Auto merge of #149709 - Urgau:overhaul-filenames, r=davidtwco

Overhaul filename handling for cross-compiler consistency

This PR overhauls the way we handle filenames in the compiler and `rmeta` in order to achieve achieve cross-compiler consistency (ie. having the same path no matter if the filename was created in the current compiler session or is coming from `rmeta`).

This is required as some parts of the compiler rely on consistent paths for the soundness of generated code (see rust-lang/rust#148328).

In order to achieved consistency multiple steps are being taken by this PR:
 - by making `RealFileName` immutable
 - by only having `SourceMap::to_real_filename` create `RealFileName`
   - currently `RealFileName` can be created from any `Path` and are remapped afterwards, which creates consistency issue
 - by also making `RealFileName` holds it's working directory, embeddable name and the remapped scopes
   - this removes the need for a `Session`, to know the current(!) scopes and cwd, which is invalid as they may not be equal to the scopes used when creating the filename

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.

This PR is split-up in multiple commits (unfortunately not atomic), but should help review the changes.

Unblocks https://github.com/rust-lang/rust/pull/147611
Fixes https://github.com/rust-lang/rust/issues/148328
This commit is contained in:
bors 2025-12-13 14:32:09 +00:00
commit 8188f6c808
60 changed files with 1129 additions and 924 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

@ -80,7 +80,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let OutlivesConstraint { sup, sub, locations, category, span, .. } = constraint;
let (name, arg) = match locations {
Locations::All(span) => {
("All", tcx.sess.source_map().span_to_embeddable_string(*span))
("All", tcx.sess.source_map().span_to_diagnostic_string(*span))
}
Locations::Single(loc) => ("Single", format!("{loc:?}")),
};

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

@ -6,7 +6,10 @@ use std::path::{Component, Path};
use cranelift_codegen::MachSrcLoc;
use cranelift_codegen::binemit::CodeOffset;
use gimli::write::{FileId, FileInfo, LineProgram, LineString, LineStringTable};
use rustc_span::{FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHashAlgorithm, hygiene};
use rustc_span::{
FileName, Pos, RemapPathScopeComponents, SourceFile, SourceFileAndLine,
SourceFileHashAlgorithm, hygiene,
};
use crate::debuginfo::FunctionDebugContext;
use crate::debuginfo::emit::address_for_func;
@ -95,7 +98,7 @@ impl DebugContext {
match &source_file.name {
FileName::Real(path) => {
let (dir_path, file_name) =
split_path_dir_and_file(path.to_path(self.filename_display_preference));
split_path_dir_and_file(path.path(RemapPathScopeComponents::DEBUGINFO));
let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
let file_name = osstr_as_utf8_bytes(file_name);

View file

@ -21,7 +21,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefIdMap;
use rustc_session::Session;
use rustc_session::config::DebugInfo;
use rustc_span::{FileNameDisplayPreference, SourceFileHash, StableSourceFileId};
use rustc_span::{RemapPathScopeComponents, SourceFileHash, StableSourceFileId};
use rustc_target::callconv::FnAbi;
pub(crate) use self::emit::{DebugReloc, DebugRelocName};
@ -44,7 +44,6 @@ pub(crate) struct DebugContext {
namespace_map: DefIdMap<UnitEntryId>,
array_size_type: Option<UnitEntryId>,
filename_display_preference: FileNameDisplayPreference,
embed_source: bool,
}
@ -102,18 +101,18 @@ impl DebugContext {
let mut dwarf = DwarfUnit::new(encoding);
use rustc_session::config::RemapPathScopeComponents;
let filename_display_preference =
tcx.sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO);
let producer = producer(tcx.sess);
let comp_dir =
tcx.sess.opts.working_dir.to_string_lossy(filename_display_preference).to_string();
let comp_dir = tcx
.sess
.source_map()
.working_dir()
.path(RemapPathScopeComponents::DEBUGINFO)
.to_string_lossy();
let (name, file_info) = match tcx.sess.local_crate_source_file() {
Some(path) => {
let name = path.to_string_lossy(filename_display_preference).to_string();
let name =
path.path(RemapPathScopeComponents::DEBUGINFO).to_string_lossy().into_owned();
(name, None)
}
None => (tcx.crate_name(LOCAL_CRATE).to_string(), None),
@ -137,7 +136,7 @@ impl DebugContext {
{
let name = dwarf.strings.add(format!("{name}/@/{cgu_name}"));
let comp_dir = dwarf.strings.add(comp_dir);
let comp_dir = dwarf.strings.add(&*comp_dir);
let root = dwarf.unit.root();
let root = dwarf.unit.get_mut(root);
@ -180,7 +179,6 @@ impl DebugContext {
stack_pointer_register,
namespace_map: DefIdMap::default(),
array_size_type,
filename_display_preference,
embed_source,
})
}

View file

@ -297,29 +297,11 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
let pos = span.lo();
let DebugLoc { file, line, col } = self.lookup_debug_loc(pos);
match file.name {
rustc_span::FileName::Real(ref name) => match *name {
rustc_span::RealFileName::LocalPath(ref name) => {
if let Some(name) = name.to_str() {
self.context.new_location(name, line as i32, col as i32)
} else {
Location::null()
}
}
rustc_span::RealFileName::Remapped {
ref local_path,
virtual_name: ref _unused,
} => {
if let Some(name) = local_path.as_ref() {
if let Some(name) = name.to_str() {
self.context.new_location(name, line as i32, col as i32)
} else {
Location::null()
}
} else {
Location::null()
}
}
},
rustc_span::FileName::Real(ref name) => self.context.new_location(
name.path(rustc_span::RemapPathScopeComponents::DEBUGINFO).to_string_lossy(),
line as i32,
col as i32,
),
_ => Location::null(),
}
}

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

@ -138,10 +138,7 @@ pub(crate) fn update_macro_stats(
if false {
let name = ExpnKind::Macro(macro_kind, name).descr();
let crate_name = &ecx.ecfg.crate_name;
let span = ecx
.sess
.source_map()
.span_to_string(span, rustc_span::FileNameDisplayPreference::Local);
let span = ecx.sess.source_map().span_to_diagnostic_string(span);
eprint!(
"\
-------------------------------\n\

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

@ -504,7 +504,7 @@ fn write_scope_tree(
"{0:1$} // at {2}",
indented_header,
ALIGN,
tcx.sess.source_map().span_to_embeddable_string(span),
tcx.sess.source_map().span_to_diagnostic_string(span),
)?;
} else {
writeln!(w, "{indented_header}")?;
@ -688,7 +688,7 @@ fn write_user_type_annotations(
"| {:?}: user_ty: {}, span: {}, inferred_ty: {}",
index.index(),
annotation.user_ty,
tcx.sess.source_map().span_to_embeddable_string(annotation.span),
tcx.sess.source_map().span_to_diagnostic_string(annotation.span),
with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)),
)?;
}
@ -1420,7 +1420,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
self.push("mir::ConstOperand");
self.push(&format!(
"+ span: {}",
self.tcx.sess.source_map().span_to_embeddable_string(*span)
self.tcx.sess.source_map().span_to_diagnostic_string(*span)
));
if let Some(user_ty) = user_ty {
self.push(&format!("+ user_ty: {user_ty:?}"));
@ -1503,7 +1503,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
}
fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
let location = tcx.sess.source_map().span_to_embeddable_string(span);
let location = tcx.sess.source_map().span_to_diagnostic_string(span);
format!("scope {} at {}", scope.index(), location,)
}

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

@ -49,7 +49,7 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In
.unwrap_or_else(|e| format!("Failed {e:?}"));
let closure_span = tcx.def_span(closure_def_id);
let src_file = tcx.sess.source_map().span_to_filename(closure_span);
let src_file = tcx.sess.source_map().span_to_diagnostic_string(closure_span);
let line_nos = tcx
.sess
.source_map()
@ -57,14 +57,7 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In
.map(|l| format!("{:?} {:?}", l.lines.first(), l.lines.last()))
.unwrap_or_else(|e| format!("{e:?}"));
if let Err(e) = writeln!(
file,
"{}, {}, {}, {:?}",
old_size,
new_size,
src_file.prefer_local(),
line_nos
) {
if let Err(e) = writeln!(file, "{}, {}, {}, {:?}", old_size, new_size, src_file, line_nos) {
eprintln!("Error writing to file {e}")
}
}

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, RealFileName, 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,25 +1353,22 @@ 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 {
fn default() -> Options {
let unstable_opts = UnstableOptions::default();
// FIXME(Urgau): This is a hack that ideally shouldn't exist, but rustdoc
// currently uses this `Default` implementation, so we have no choice but
// to create a default working directory.
let working_dir = {
let working_dir = std::env::current_dir().unwrap();
let file_mapping = file_path_mapping(Vec::new(), &unstable_opts);
file_mapping.to_real_filename(&RealFileName::empty(), &working_dir)
};
Options {
assert_incr_state: None,
crate_types: Vec::new(),
@ -1404,7 +1385,7 @@ impl Default for Options {
test: false,
incremental: None,
untracked_state_hash: Default::default(),
unstable_opts: Default::default(),
unstable_opts,
prints: Vec::new(),
cg: Default::default(),
error_format: ErrorOutputType::default(),
@ -1428,7 +1409,7 @@ impl Default for Options {
json_unused_externs: JsonUnusedExterns::No,
json_future_incompat: false,
pretty: None,
working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
working_dir,
color: ColorConfig::Auto,
logical_env: FxIndexMap::default(),
verbose: false,
@ -2782,12 +2763,16 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
.collect()
};
let working_dir = std::env::current_dir().unwrap_or_else(|e| {
early_dcx.early_fatal(format!("Current directory is invalid: {e}"));
});
// Ideally we would use `SourceMap::working_dir` instead, but we don't have access to it
// so we manually create the potentially-remapped working directory
let working_dir = {
let working_dir = std::env::current_dir().unwrap_or_else(|e| {
early_dcx.early_fatal(format!("Current directory is invalid: {e}"));
});
let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts);
let working_dir = file_mapping.to_real_filename(&working_dir);
let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts);
file_mapping.to_real_filename(&RealFileName::empty(), &working_dir)
};
let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals;
@ -3115,8 +3100,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 +3113,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::{RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm};
use rustc_target::spec::{
CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy,
RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility,
@ -492,7 +492,9 @@ top_level_options!(
pretty: Option<PpMode> [UNTRACKED],
/// The (potentially remapped) working directory
#[rustc_lint_opt_deny_field_access("use `SourceMap::working_dir` instead of this field")]
working_dir: RealFileName [TRACKED],
color: ColorConfig [UNTRACKED],
verbose: bool [TRACKED_NO_CRATE_HASH],

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

@ -105,6 +105,9 @@ pub trait FileLoader {
/// Read the contents of a potentially non-UTF-8 file into memory.
/// We don't normalize binary files, so we can start in an Arc.
fn read_binary_file(&self, path: &Path) -> io::Result<Arc<[u8]>>;
/// Current working directory
fn current_directory(&self) -> io::Result<PathBuf>;
}
/// A FileLoader that uses std::fs to load real files.
@ -170,6 +173,10 @@ impl FileLoader for RealFileLoader {
file.read_to_end(&mut bytes)?;
Ok(bytes.into())
}
fn current_directory(&self) -> io::Result<PathBuf> {
std::env::current_dir()
}
}
// _____________________________________________________________________________
@ -198,6 +205,9 @@ pub struct SourceMap {
// `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`.
path_mapping: FilePathMapping,
/// Current working directory
working_dir: RealFileName,
/// The algorithm used for hashing the contents of each source file.
hash_kind: SourceFileHashAlgorithm,
@ -221,8 +231,14 @@ impl SourceMap {
pub fn with_inputs(
SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }: SourceMapInputs,
) -> SourceMap {
let cwd = file_loader
.current_directory()
.expect("expecting a current working directory to exist");
let working_dir = path_mapping.to_real_filename(&RealFileName::empty(), &cwd);
debug!(?working_dir);
SourceMap {
files: Default::default(),
working_dir,
file_loader: IntoDynSyncSend(file_loader),
path_mapping,
hash_kind,
@ -234,13 +250,17 @@ impl SourceMap {
&self.path_mapping
}
pub fn working_dir(&self) -> &RealFileName {
&self.working_dir
}
pub fn file_exists(&self, path: &Path) -> bool {
self.file_loader.file_exists(path)
}
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))
}
@ -257,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(
@ -325,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) {
@ -424,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}") }
)
}
@ -459,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 {
@ -476,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 {
@ -1025,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));
}
}
@ -1060,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())
}
};
@ -1115,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.
@ -1189,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

@ -25,7 +25,7 @@ use rustc_resolve::rustdoc::{
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{Symbol, kw, sym};
use rustc_span::{DUMMY_SP, FileName, Loc};
use rustc_span::{DUMMY_SP, FileName, Loc, RemapPathScopeComponents};
use tracing::{debug, trace};
use {rustc_ast as ast, rustc_hir as hir};
@ -148,10 +148,17 @@ impl ExternalCrate {
pub(crate) fn src_root(&self, tcx: TyCtxt<'_>) -> PathBuf {
match self.src(tcx) {
FileName::Real(ref p) => match p.local_path_if_available().parent() {
Some(p) => p.to_path_buf(),
None => PathBuf::new(),
},
FileName::Real(ref p) => {
match p
.local_path()
.or(Some(p.path(RemapPathScopeComponents::MACRO)))
.unwrap()
.parent()
{
Some(p) => p.to_path_buf(),
None => PathBuf::new(),
}
}
_ => PathBuf::new(),
}
}

View file

@ -29,7 +29,7 @@ use rustc_middle::ty::TyCtxt;
use rustc_session::config::{self, CrateType, ErrorOutputType, Input};
use rustc_session::lint;
use rustc_span::edition::Edition;
use rustc_span::{FileName, Span};
use rustc_span::{FileName, RemapPathScopeComponents, Span};
use rustc_target::spec::{Target, TargetTuple};
use tempfile::{Builder as TempFileBuilder, TempDir};
use tracing::debug;
@ -971,14 +971,7 @@ impl ScrapedDocTest {
}
fn path(&self) -> PathBuf {
match &self.filename {
FileName::Real(path) => {
if let Some(local_path) = path.local_path() {
local_path.to_path_buf()
} else {
// Somehow we got the filename from the metadata of another crate, should never happen
unreachable!("doctest from a different crate");
}
}
FileName::Real(name) => name.path(RemapPathScopeComponents::DIAGNOSTICS).to_path_buf(),
_ => PathBuf::from(r"doctest.rs"),
}
}
@ -1025,7 +1018,7 @@ impl CreateRunnableDocTests {
// For example `module/file.rs` would become `module_file_rs`
let file = scraped_test
.filename
.prefer_local()
.prefer_local_unconditionally()
.to_string_lossy()
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })

View file

@ -5,7 +5,8 @@ use std::sync::{Arc, Mutex};
use rustc_errors::DiagCtxtHandle;
use rustc_session::config::Input;
use rustc_span::{DUMMY_SP, FileName};
use rustc_span::source_map::FilePathMapping;
use rustc_span::{DUMMY_SP, FileName, RealFileName};
use tempfile::tempdir;
use super::{
@ -106,8 +107,12 @@ pub(crate) fn test(input: &Input, options: Options, dcx: DiagCtxtHandle<'_>) ->
cur_path: vec![],
filename: input
.opt_path()
.map(ToOwned::to_owned)
.map(FileName::from)
.map(|f| {
// We don't have access to a rustc Session so let's just use a dummy
// filepath mapping to create a real filename.
let file_mapping = FilePathMapping::empty();
FileName::Real(file_mapping.to_real_filename(&RealFileName::empty(), f))
})
.unwrap_or(FileName::Custom("input".to_owned())),
};
let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());

View file

@ -1,7 +1,6 @@
//! Doctest functionality used only for doctests in `.rs` source files.
use std::cell::Cell;
use std::env;
use std::str::FromStr;
use std::sync::Arc;
@ -31,15 +30,6 @@ struct RustCollector {
impl RustCollector {
fn get_filename(&self) -> FileName {
let filename = self.source_map.span_to_filename(self.position);
if let FileName::Real(ref filename) = filename {
let path = filename.remapped_path_if_available();
// Strip the cwd prefix from the path. This will likely exist if
// the path was not remapped.
let path = env::current_dir()
.map(|cur_dir| path.strip_prefix(&cur_dir).unwrap_or(path))
.unwrap_or(path);
return path.to_owned().into();
}
filename
}

View file

@ -365,7 +365,7 @@ impl<'tcx> Context<'tcx> {
// We can safely ignore synthetic `SourceFile`s.
let file = match span.filename(self.sess()) {
FileName::Real(ref path) => path.local_path_if_available().to_path_buf(),
FileName::Real(ref path) => path.local_path()?.to_path_buf(),
_ => return None,
};
let file = &file;
@ -499,7 +499,7 @@ impl<'tcx> Context<'tcx> {
} = options;
let src_root = match krate.src(tcx) {
FileName::Real(ref p) => match p.local_path_if_available().parent() {
FileName::Real(ref p) => match p.local_path().map(|p| p.parent()).flatten() {
Some(p) => p.to_path_buf(),
None => PathBuf::new(),
},

View file

@ -60,7 +60,7 @@ use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, Stable
use rustc_middle::ty::print::PrintTraitRefExt;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{Symbol, sym};
use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName};
use rustc_span::{BytePos, DUMMY_SP, FileName};
use tracing::{debug, info};
pub(crate) use self::context::*;
@ -2772,7 +2772,7 @@ fn render_call_locations<W: fmt::Write>(
files
.iter()
.find(|file| match &file.name {
FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path,
FileName::Real(real) => real.local_path().map_or(false, |p| p == rel_path),
_ => false,
})
.map(|file| file.start_pos)

View file

@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::{FileName, FileNameDisplayPreference, RealFileName};
use rustc_span::{FileName, RealFileName, RemapPathScopeComponents};
use tracing::info;
use super::render::Context;
@ -148,7 +148,10 @@ impl DocVisitor<'_> for SourceCollector<'_, '_> {
span,
format!(
"failed to render source code for `{filename}`: {e}",
filename = filename.to_string_lossy(FileNameDisplayPreference::Local),
filename = filename
.path(RemapPathScopeComponents::DIAGNOSTICS)
.to_string_lossy()
.into_owned(),
),
);
false
@ -224,10 +227,7 @@ impl SourceCollector<'_, '_> {
cur.push(&fname);
let title = format!("{} - source", src_fname.to_string_lossy());
let desc = format!(
"Source of the Rust file `{}`.",
file.to_string_lossy(FileNameDisplayPreference::Remapped)
);
let desc = format!("Source of the Rust file `{}`.", p.to_string_lossy());
let page = layout::Page {
title: &title,
short_title: &src_fname.to_string_lossy(),

View file

@ -7,7 +7,7 @@ use rustc_hir as hir;
use rustc_lint::builtin::MISSING_DOCS;
use rustc_middle::lint::{LevelAndSource, LintLevelSource};
use rustc_session::lint;
use rustc_span::FileName;
use rustc_span::{FileName, RemapPathScopeComponents};
use serde::Serialize;
use tracing::debug;
@ -124,7 +124,7 @@ impl CoverageCalculator<'_, '_> {
&self
.items
.iter()
.map(|(k, v)| (k.prefer_local().to_string(), v))
.map(|(k, v)| (k.prefer_local_unconditionally().to_string(), v))
.collect::<BTreeMap<String, &ItemCount>>(),
)
.expect("failed to convert JSON data to string")
@ -167,7 +167,11 @@ impl CoverageCalculator<'_, '_> {
for (file, &count) in &self.items {
if let Some(percentage) = count.percentage() {
print_table_record(
&limit_filename_len(file.prefer_local().to_string_lossy().into()),
&limit_filename_len(
file.display(RemapPathScopeComponents::DIAGNOSTICS)
.to_string_lossy()
.into(),
),
count,
percentage,
count.examples_percentage().unwrap_or(0.),

View file

@ -81,7 +81,7 @@ pub struct ModStyle {
impl EarlyLintPass for ModStyle {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
self.working_dir = cx.sess().opts.working_dir.local_path().map(Path::to_path_buf);
self.working_dir = cx.sess().source_map().working_dir().local_path().map(Path::to_path_buf);
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {

View file

@ -13,7 +13,7 @@ use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::source_map::{SourceMap, original_sp};
use rustc_span::{
BytePos, DUMMY_SP, DesugaringKind, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine,
BytePos, DUMMY_SP, DesugaringKind, Pos, RelativeBytePos, SourceFile, SourceFileAndLine,
Span, SpanData, SyntaxContext, hygiene,
};
use std::borrow::Cow;
@ -268,7 +268,7 @@ fn map_range(
debug_assert!(
range.start <= text.len() && range.end <= text.len(),
"Range `{range:?}` is outside the source file (file `{}`, length `{}`)",
src.sf.name.display(FileNameDisplayPreference::Local),
src.sf.name.prefer_local_unconditionally(),
text.len(),
);
debug_assert!(range.start <= range.end, "Range `{range:?}` has overlapping bounds");

View file

@ -627,7 +627,7 @@ pub fn report_msg<'tcx>(
err.subdiagnostic(frame_info.as_note(machine.tcx));
} else {
let sm = sess.source_map();
let span = sm.span_to_embeddable_string(frame_info.span);
let span = sm.span_to_diagnostic_string(frame_info.span);
err.note(format!("{frame_info} at {span}"));
}
}

View file

@ -28,7 +28,15 @@ pub enum FileName {
impl From<rustc_span::FileName> for FileName {
fn from(name: rustc_span::FileName) -> FileName {
match name {
rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(p)) => FileName::Real(p),
rustc_span::FileName::Real(real) => {
if let Some(p) = real.into_local_path() {
FileName::Real(p)
} else {
// rustfmt does not remap filenames; the local path should always
// remain accessible.
unreachable!()
}
}
rustc_span::FileName::Custom(ref f) if f == "stdin" => FileName::Stdin,
_ => unreachable!(),
}

View file

@ -58,19 +58,19 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
}
if let Some(primary_span) = &diag.span.primary_span() {
let file_name = self.source_map.span_to_filename(*primary_span);
if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) =
file_name
{
if self
.ignore_path_set
.is_match(&FileName::Real(path.to_path_buf()))
{
if !self.has_non_ignorable_parser_errors {
self.can_reset.store(true, Ordering::Release);
if let rustc_span::FileName::Real(real) = file_name {
if let Some(path) = real.local_path() {
if self
.ignore_path_set
.is_match(&FileName::Real(path.to_path_buf()))
{
if !self.has_non_ignorable_parser_errors {
self.can_reset.store(true, Ordering::Release);
}
return;
}
return;
}
};
}
}
self.handle_non_ignoreable_error(diag, registry);
}
@ -181,7 +181,10 @@ impl ParseSess {
self.raw_psess
.source_map()
.get_source_file(&rustc_span::FileName::Real(
rustc_span::RealFileName::LocalPath(path.to_path_buf()),
self.raw_psess
.source_map()
.path_mapping()
.to_real_filename(self.raw_psess.source_map().working_dir(), path),
))
.is_some()
}
@ -246,10 +249,20 @@ impl ParseSess {
)
}
pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Arc<String>> {
pub(crate) fn get_original_snippet(&self, filename: &FileName) -> Option<Arc<String>> {
let rustc_filename = match filename {
FileName::Real(path) => rustc_span::FileName::Real(
self.raw_psess
.source_map()
.path_mapping()
.to_real_filename(self.raw_psess.source_map().working_dir(), path),
),
FileName::Stdin => rustc_span::FileName::Custom("stdin".to_owned()),
};
self.raw_psess
.source_map()
.get_source_file(&file_name.into())
.get_source_file(&rustc_filename)
.and_then(|source_file| source_file.src.clone())
}
}
@ -313,7 +326,7 @@ mod tests {
use crate::config::IgnoreList;
use crate::utils::mk_sp;
use rustc_errors::MultiSpan;
use rustc_span::{FileName as SourceMapFileName, RealFileName};
use rustc_span::FileName as SourceMapFileName;
use std::path::PathBuf;
use std::sync::atomic::AtomicU32;
@ -372,6 +385,13 @@ mod tests {
.ignore()
}
fn filename(sm: &SourceMap, path: &str) -> SourceMapFileName {
SourceMapFileName::Real(
sm.path_mapping()
.to_real_filename(sm.working_dir(), PathBuf::from(path)),
)
}
#[test]
fn handles_fatal_parse_error_in_ignored_file() {
let num_emitted_errors = Arc::new(AtomicU32::new(0));
@ -380,10 +400,7 @@ mod tests {
let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
let source =
String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
source_map.new_source_file(
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
source,
);
source_map.new_source_file(filename(&source_map, "foo.rs"), source);
let registry = Registry::new(&[]);
let mut emitter = build_emitter(
Arc::clone(&num_emitted_errors),
@ -406,10 +423,7 @@ mod tests {
let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
let source = String::from(r#"pub fn bar() { 1x; }"#);
source_map.new_source_file(
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
source,
);
source_map.new_source_file(filename(&source_map, "foo.rs"), source);
let registry = Registry::new(&[]);
let mut emitter = build_emitter(
Arc::clone(&num_emitted_errors),
@ -431,10 +445,7 @@ mod tests {
let can_reset_errors = Arc::new(AtomicBool::new(false));
let source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
let source = String::from(r#"pub fn bar() { 1x; }"#);
source_map.new_source_file(
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
source,
);
source_map.new_source_file(filename(&source_map, "foo.rs"), source);
let registry = Registry::new(&[]);
let mut emitter = build_emitter(
Arc::clone(&num_emitted_errors),
@ -460,18 +471,9 @@ mod tests {
let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
let fatal_source =
String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
source_map.new_source_file(
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))),
bar_source,
);
source_map.new_source_file(
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
foo_source,
);
source_map.new_source_file(
SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
fatal_source,
);
source_map.new_source_file(filename(&source_map, "bar.rs"), bar_source);
source_map.new_source_file(filename(&source_map, "foo.rs"), foo_source);
source_map.new_source_file(filename(&source_map, "fatal.rs"), fatal_source);
let registry = Registry::new(&[]);
let mut emitter = build_emitter(
Arc::clone(&num_emitted_errors),

View file

@ -65,18 +65,6 @@ where
}
}
#[allow(non_local_definitions)]
impl From<&FileName> for rustc_span::FileName {
fn from(filename: &FileName) -> rustc_span::FileName {
match filename {
FileName::Real(path) => {
rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(path.to_owned()))
}
FileName::Stdin => rustc_span::FileName::Custom("stdin".to_owned()),
}
}
}
// SourceFile's in the SourceMap will always have Unix-style line endings
// See: https://github.com/rust-lang/rustfmt/issues/3850
// So if the user has explicitly overridden the rustfmt `newline_style`

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

@ -0,0 +1,21 @@
// Reproducer from https://github.com/rust-lang/rust/issues/148328#issuecomment-3473688412
#[inline(always)]
pub const fn the_path() -> &'static str {
std::panic::Location::caller().file()
}
#[inline(never)]
pub fn the_path2() -> &'static str {
const { std::panic::Location::caller().file() }
}
// Reproducer from https://github.com/rust-lang/rust/issues/148328#issuecomment-3473761194
pub const fn the_path_len() -> usize {
std::panic::Location::caller().file().len()
}
pub type Array = [u8; the_path_len()];
pub fn the_zeroed_path_len_array() -> Array {
[0; _]
}

View file

@ -0,0 +1,115 @@
//@ ignore-cross-compile (relocations in generic ELF against `arm-unknown-linux-gnueabihf`)
//@ needs-target-std
use run_make_support::{bin_name, cwd, run, rustc};
fn main() {
// No remapping - relative paths
{
let runner_bin = bin_name("runner-no-remap-rel-paths");
let mut location_caller = rustc();
location_caller.crate_type("lib").input("location-caller.rs");
location_caller.run();
let mut runner = rustc();
runner.crate_type("bin").input("runner.rs").output(&runner_bin);
runner.run();
run(&runner_bin);
}
// No remapping - absolute paths
{
let runner_bin = bin_name("runner-no-remap-abs-paths");
let mut location_caller = rustc();
location_caller.crate_type("lib").input(cwd().join("location-caller.rs"));
location_caller.run();
let mut runner = rustc();
runner.crate_type("bin").input(cwd().join("runner.rs")).output(&runner_bin);
runner.run();
run(&runner_bin);
}
// No remapping - mixed paths
{
let runner_bin = bin_name("runner-no-remap-mixed-paths");
let mut location_caller = rustc();
location_caller.crate_type("lib").input(cwd().join("location-caller.rs"));
location_caller.run();
let mut runner = rustc();
runner.crate_type("bin").input("runner.rs").output(&runner_bin);
runner.run();
run(&runner_bin);
}
// Remapping current working directory
{
let runner_bin = bin_name("runner-remap-cwd");
let mut location_caller = rustc();
location_caller
.crate_type("lib")
.remap_path_prefix(cwd(), "/remapped")
.input(cwd().join("location-caller.rs"));
location_caller.run();
let mut runner = rustc();
runner
.crate_type("bin")
.remap_path_prefix(cwd(), "/remapped")
.input(cwd().join("runner.rs"))
.output(&runner_bin);
runner.run();
run(&runner_bin);
}
// Remapping current working directory - only in the dependency
{
let runner_bin = bin_name("runner-remap-cwd-only-dep");
let mut location_caller = rustc();
location_caller
.crate_type("lib")
.remap_path_prefix(cwd(), "/remapped")
.input(cwd().join("location-caller.rs"));
location_caller.run();
let mut runner = rustc();
runner.crate_type("bin").input(cwd().join("runner.rs")).output(&runner_bin);
runner.run();
run(&runner_bin);
}
// Remapping current working directory - different scopes
{
let runner_bin = bin_name("runner-remap-cwd-diff-scope");
let mut location_caller = rustc();
location_caller
.crate_type("lib")
.remap_path_prefix(cwd(), "/remapped")
.arg("-Zremap-path-scope=object")
.input(cwd().join("location-caller.rs"));
location_caller.run();
let mut runner = rustc();
runner
.crate_type("bin")
.remap_path_prefix(cwd(), "/remapped")
.arg("-Zremap-path-scope=diagnostics")
.input(cwd().join("runner.rs"))
.output(&runner_bin);
runner.run();
run(&runner_bin);
}
}

View file

@ -0,0 +1,19 @@
// Verifies that the paths are the same and consistent between this crate and location_caller crate.
//
// https://github.com/rust-lang/rust/issues/148328
extern crate location_caller;
fn main() {
{
// Assert both paths are the same
let the_path = location_caller::the_path();
let the_path2 = location_caller::the_path2();
assert_eq!(the_path, the_path2);
}
{
// Let's make sure we don't read OOB memory
println!("{:?}", location_caller::the_zeroed_path_len_array());
}
}

View file

@ -3,6 +3,7 @@
//@ compile-flags:-Z unstable-options --output-format=doctest
//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR"
//@ normalize-stdout: "[A-Z]:[\\/](?:[^\\/]+[\\/])*?\$DIR" -> "$$DIR"
//@ check-pass
//! ```

View file

@ -1 +1 @@
{"format_version":2,"doctests":[{"file":"$DIR/extract-doctests-result.rs","line":8,"doctest_attributes":{"original":"","should_panic":false,"no_run":false,"ignore":"None","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nOk(())","doctest_code":{"crate_level":"#![allow(unused)]\n","code":"let x = 12;\nOk(())","wrapper":{"before":"fn main() { fn _inner() -> core::result::Result<(), impl core::fmt::Debug> {\n","after":"\n} _inner().unwrap() }","returns_result":true}},"name":"$DIR/extract-doctests-result.rs - (line 8)"}]}
{"format_version":2,"doctests":[{"file":"$DIR/extract-doctests-result.rs","line":9,"doctest_attributes":{"original":"","should_panic":false,"no_run":false,"ignore":"None","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nOk(())","doctest_code":{"crate_level":"#![allow(unused)]\n","code":"let x = 12;\nOk(())","wrapper":{"before":"fn main() { fn _inner() -> core::result::Result<(), impl core::fmt::Debug> {\n","after":"\n} _inner().unwrap() }","returns_result":true}},"name":"$DIR/extract-doctests-result.rs - (line 9)"}]}

View file

@ -3,6 +3,7 @@
//@ compile-flags:-Z unstable-options --output-format=doctest
//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR"
//@ normalize-stdout: "[A-Z]:[\\/](?:[^\\/]+[\\/])*?\$DIR" -> "$$DIR"
//@ check-pass
//! ```ignore (checking attributes)

View file

@ -1 +1 @@
{"format_version":2,"doctests":[{"file":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","doctest_code":{"crate_level":"#![allow(unused)]\n","code":"let x = 12;\nlet y = 14;","wrapper":{"before":"fn main() {\n","after":"\n}","returns_result":false}},"name":"$DIR/extract-doctests.rs - (line 8)"},{"file":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_css_classes":[],"unknown":[]},"original_code":"let","doctest_code":null,"name":"$DIR/extract-doctests.rs - (line 13)"}]}
{"format_version":2,"doctests":[{"file":"$DIR/extract-doctests.rs","line":9,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","doctest_code":{"crate_level":"#![allow(unused)]\n","code":"let x = 12;\nlet y = 14;","wrapper":{"before":"fn main() {\n","after":"\n}","returns_result":false}},"name":"$DIR/extract-doctests.rs - (line 9)"},{"file":"$DIR/extract-doctests.rs","line":14,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_css_classes":[],"unknown":[]},"original_code":"let","doctest_code":null,"name":"$DIR/extract-doctests.rs - (line 14)"}]}

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`