From 8cbfb26383347cf4970a488177acfdc35c30984b Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 28 Nov 2025 00:01:51 +0100 Subject: [PATCH] Overhaul filename handling for cross-compiler consistency This commit refactors `SourceMap` and most importantly `RealFileName` to make it self-contained in order to achieve cross-compiler consistency. This is achieved: - by making `RealFileName` immutable - by only having `SourceMap::to_real_filename` create `RealFileName` - by also making `RealFileName` holds it's working directory, it's embeddable name and the remapped scopes - by making most `FileName` and `RealFileName` methods take a scope as an argument In order for `SourceMap::to_real_filename` to know which scopes to apply `FilePathMapping` now takes the current remapping scopes to apply, which makes `FileNameDisplayPreference` and company useless and are removed. The scopes type `RemapPathScopeComponents` was moved from `rustc_session::config` to `rustc_span`. The previous system for scoping the local/remapped filenames `RemapFileNameExt::for_scope` is no longer useful as it's replaced by methods on `FileName` and `RealFileName`. --- Cargo.lock | 2 +- .../rustc_builtin_macros/src/source_util.rs | 7 +- compiler/rustc_builtin_macros/src/test.rs | 4 +- compiler/rustc_codegen_llvm/src/back/write.rs | 15 +- .../src/coverageinfo/mapgen.rs | 16 +- .../src/debuginfo/metadata.rs | 109 ++-- .../src/const_eval/machine.rs | 9 +- compiler/rustc_driver_impl/src/pretty.rs | 2 +- compiler/rustc_errors/src/json.rs | 14 +- compiler/rustc_errors/src/json/tests.rs | 6 +- compiler/rustc_expand/src/expand.rs | 2 +- compiler/rustc_interface/src/passes.rs | 10 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 52 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 8 +- compiler/rustc_middle/src/mir/consts.rs | 10 +- compiler/rustc_middle/src/ty/print/pretty.rs | 38 +- compiler/rustc_parse/src/lib.rs | 8 +- compiler/rustc_parse/src/parser/tests.rs | 20 +- compiler/rustc_passes/src/entry.rs | 7 +- .../rustc_public_bridge/src/context/impls.rs | 9 +- compiler/rustc_resolve/src/rustdoc/tests.rs | 10 +- compiler/rustc_session/Cargo.toml | 1 - compiler/rustc_session/src/config.rs | 58 +-- compiler/rustc_session/src/options.rs | 2 +- compiler/rustc_session/src/session.rs | 71 +-- compiler/rustc_span/Cargo.toml | 1 + compiler/rustc_span/src/lib.rs | 339 +++++++++---- compiler/rustc_span/src/profiling.rs | 3 +- compiler/rustc_span/src/source_map.rs | 256 ++++------ compiler/rustc_span/src/source_map/tests.rs | 472 ++++++++++++------ tests/run-make/duplicate-dependency/rmake.rs | 6 +- ...refix-diagnostics.only-diag-in-deps.stderr | 2 +- 32 files changed, 805 insertions(+), 764 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d818d87e0804..b7cca34528f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 16adaab15c52..d49f4ded010b 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -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()), ))) } diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index f31ad4f591b1..6f14385d5d30 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -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(), }; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b131de1df8ba..2c4943e835a6 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -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| -> 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() }; diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index b3a11f8b12bf..bc54657a380a 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -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()); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 5535ebe65859..dc941cb41c56 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -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. - // `/src/lib.rs`), so if we emit it without taking - // the working directory into account, downstream tooling will interpret it - // as `//src/lib.rs`, which - // makes no sense. Usually in such cases the working directory will also be - // remapped to `` or some other prefix of the path - // we are remapping, so we end up with - // `//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. + // `/src/lib.rs`), so if we emit it without taking + // the working directory into account, downstream tooling will interpret it + // as `//src/lib.rs`, which + // makes no sense. Usually in such cases the working directory will also be + // remapped to `` or some other prefix of the path + // we are remapping, so we end up with + // `//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(); diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index fccb6b171b1c..0c677b34df7b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -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(), diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 1604b704033b..962d035db862 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -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) diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index ce5c830bbfcd..3b8c8baa5eff 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -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 } }; diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 79bb5054dfef..30be06ae0bd3 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -36,11 +36,15 @@ impl Write for Shared { } } +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); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 33431556c76c..6422779e13c9 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -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(); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ddfec9f886a6..9acb69f5940d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -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. diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 65cbdc675962..0a6e5f153dde 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -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), + )) } } }; diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 2069c06c3f77..b583c73b5a6b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -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); } _ => { diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 6985cc7ddcfa..fe352df3b9f0 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -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, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 798e14c6f378..c4f2917ad9e5 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -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> { "", // 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; diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 0992bee32f32..df8f970e0599 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -129,7 +129,13 @@ pub fn utf8_error( 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 { diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 9b157cb6c7bf..a46fcd30fef4 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -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(); diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 8ed2c2a89266..c02a01c1b823 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -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() diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index 26728a34b84a..56aa22378072 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -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`. diff --git a/compiler/rustc_resolve/src/rustdoc/tests.rs b/compiler/rustc_resolve/src/rustdoc/tests.rs index 9fea6f6807e4..7ad0a5003e4b 100644 --- a/compiler/rustc_resolve/src/rustdoc/tests.rs +++ b/compiler/rustc_resolve/src/rustdoc/tests.rs @@ -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, "⚠", diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 0516982aeabb..aebac3880d2f 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -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" } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 35d9b77f2ea8..b5b19c64951b 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -24,10 +24,7 @@ use rustc_hashes::Hash64; use rustc_macros::{BlobDecodable, Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::FilePathMapping; -use rustc_span::{ - FileName, FileNameDisplayPreference, FileNameEmbeddablePreference, RealFileName, - SourceFileHashAlgorithm, Symbol, sym, -}; +use rustc_span::{FileName, SourceFileHashAlgorithm, Symbol, sym}; use rustc_target::spec::{ FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo, Target, TargetTuple, @@ -1022,9 +1019,15 @@ impl Input { "rust_out" } - pub fn source_name(&self) -> FileName { + pub fn file_name(&self, session: &Session) -> FileName { match *self { - Input::File(ref ifile) => ifile.clone().into(), + Input::File(ref ifile) => FileName::Real( + session + .psess + .source_map() + .path_mapping() + .to_real_filename(session.psess.source_map().working_dir(), ifile.as_path()), + ), Input::Str { ref name, .. } => name.clone(), } } @@ -1312,25 +1315,6 @@ impl OutputFilenames { } } -bitflags::bitflags! { - /// Scopes used to determined if it need to apply to --remap-path-prefix - #[derive(Clone, Copy, PartialEq, Eq, Hash)] - pub struct RemapPathScopeComponents: u8 { - /// Apply remappings to the expansion of std::file!() macro - const MACRO = 1 << 0; - /// Apply remappings to printed compiler diagnostics - const DIAGNOSTICS = 1 << 1; - /// Apply remappings to debug information - const DEBUGINFO = 1 << 3; - /// Apply remappings to coverage information - const COVERAGE = 1 << 4; - - /// An alias for `macro`, `debuginfo` and `coverage`. This ensures all paths in compiled - /// executables, libraries and objects are remapped but not elsewhere. - const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits() | Self::COVERAGE.bits(); - } -} - #[derive(Clone, Debug)] pub struct Sysroot { pub explicit: Option, @@ -1369,21 +1353,7 @@ fn file_path_mapping( remap_path_prefix: Vec<(PathBuf, PathBuf)>, unstable_opts: &UnstableOptions, ) -> FilePathMapping { - FilePathMapping::new( - remap_path_prefix.clone(), - if unstable_opts.remap_path_scope.contains(RemapPathScopeComponents::DIAGNOSTICS) - && !remap_path_prefix.is_empty() - { - FileNameDisplayPreference::Remapped - } else { - FileNameDisplayPreference::Local - }, - if unstable_opts.remap_path_scope.is_all() { - FileNameEmbeddablePreference::RemappedOnly - } else { - FileNameEmbeddablePreference::LocalAndRemapped - }, - ) + FilePathMapping::new(remap_path_prefix.clone(), unstable_opts.remap_path_scope) } impl Default for Options { @@ -3115,8 +3085,8 @@ pub(crate) mod dep_tracking { use rustc_errors::LanguageIdentifier; use rustc_feature::UnstableFeatures; use rustc_hashes::Hash64; - use rustc_span::RealFileName; use rustc_span::edition::Edition; + use rustc_span::{RealFileName, RemapPathScopeComponents}; use rustc_target::spec::{ CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple, @@ -3128,9 +3098,9 @@ pub(crate) mod dep_tracking { CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, - OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, - RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, - SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks, + SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, + WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 92dd4a23125c..d7ac39ecc9e8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -12,7 +12,7 @@ use rustc_feature::UnstableFeatures; use rustc_hashes::Hash64; use rustc_macros::{BlobDecodable, Encodable}; use rustc_span::edition::Edition; -use rustc_span::{RealFileName, SourceFileHashAlgorithm}; +use rustc_span::{RemapPathScopeComponents, SourceFileHashAlgorithm}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index acc65fc11a2a..14b80099bafe 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -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 { - 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 { @@ -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 { }; 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() - } - } -} diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 43a2d692577e..bf0cad63e543 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -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" } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 6a359c5f4656..49b2e0c1ff1a 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -221,99 +221,227 @@ pub fn with_metavar_spans(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 Encodable for RemapPathScopeComponents { + fn encode(&self, s: &mut E) { + s.emit_u8(self.bits()); + } +} + +impl Decodable 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, - /// `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, + /// 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(&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 { - 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 { + 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 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 { match self { FileName::Real(path) => path.into_local_path(), diff --git a/compiler/rustc_span/src/profiling.rs b/compiler/rustc_span/src/profiling.rs index c5a8bd3b15be..ab47e441cc35 100644 --- a/compiler/rustc_span/src/profiling.rs +++ b/compiler/rustc_span/src/profiling.rs @@ -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 + Into, { 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)); } } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 090442bf64bd..35694be9e492 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -234,7 +234,7 @@ impl SourceMap { let cwd = file_loader .current_directory() .expect("expecting a current working directory to exist"); - let working_dir = cwd.to_path_buf().into(); + let working_dir = path_mapping.to_real_filename(&RealFileName::empty(), &cwd); debug!(?working_dir); SourceMap { files: Default::default(), @@ -260,7 +260,7 @@ impl SourceMap { pub fn load_file(&self, path: &Path) -> io::Result> { let src = self.file_loader.read_file(path)?; - let filename = path.to_owned().into(); + let filename = FileName::Real(self.path_mapping.to_real_filename(&self.working_dir, path)); Ok(self.new_source_file(filename, src)) } @@ -277,7 +277,8 @@ impl SourceMap { // via `mod`, so we try to use real file contents and not just an // empty string. let text = std::str::from_utf8(&bytes).unwrap_or("").to_string(); - let file = self.new_source_file(path.to_owned().into(), text); + let filename = FileName::Real(self.path_mapping.to_real_filename(&self.working_dir, path)); + let file = self.new_source_file(filename, text); Ok(( bytes, Span::new( @@ -345,7 +346,6 @@ impl SourceMap { // Note that filename may not be a valid path, eg it may be `` etc, // but this is okay because the directory determined by `path.pop()` will // be empty, so the working directory will be used. - let (filename, _) = self.path_mapping.map_filename_prefix(&filename); let stable_id = StableSourceFileId::from_filename_in_current_crate(&filename); match self.source_file_by_stable_id(stable_id) { @@ -444,25 +444,36 @@ impl SourceMap { } } - pub fn span_to_string( + pub fn span_to_string(&self, sp: Span, display_scope: RemapPathScopeComponents) -> String { + self.span_to_string_ext(sp, display_scope, false) + } + + pub fn span_to_short_string( &self, sp: Span, - filename_display_pref: FileNameDisplayPreference, + display_scope: RemapPathScopeComponents, + ) -> String { + self.span_to_string_ext(sp, display_scope, true) + } + + fn span_to_string_ext( + &self, + sp: Span, + display_scope: RemapPathScopeComponents, + short: bool, ) -> String { let (source_file, lo_line, lo_col, hi_line, hi_col) = self.span_to_location_info(sp); let file_name = match source_file { - Some(sf) => sf.name.display(filename_display_pref).to_string(), + Some(sf) => { + if short { sf.name.short() } else { sf.name.display(display_scope) }.to_string() + } None => return "no-location".to_string(), }; format!( "{file_name}:{lo_line}:{lo_col}{}", - if let FileNameDisplayPreference::Short = filename_display_pref { - String::new() - } else { - format!(": {hi_line}:{hi_col}") - } + if short { String::new() } else { format!(": {hi_line}:{hi_col}") } ) } @@ -479,16 +490,11 @@ impl SourceMap { (Some(lo.file), lo.line, lo.col.to_usize() + 1, hi.line, hi.col.to_usize() + 1) } - /// Format the span location suitable for embedding in build artifacts - pub fn span_to_embeddable_string(&self, sp: Span) -> String { - self.span_to_string(sp, FileNameDisplayPreference::Remapped) - } - /// Format the span location to be printed in diagnostics. Must not be emitted /// to build artifacts as this may leak local file paths. Use span_to_embeddable_string /// for string suitable for embedding. pub fn span_to_diagnostic_string(&self, sp: Span) -> String { - self.span_to_string(sp, self.path_mapping.filename_display_for_diagnostics) + self.span_to_string(sp, RemapPathScopeComponents::DIAGNOSTICS) } pub fn span_to_filename(&self, sp: Span) -> FileName { @@ -496,7 +502,7 @@ impl SourceMap { } pub fn filename_for_diagnostics<'a>(&self, filename: &'a FileName) -> FileNameDisplay<'a> { - filename.display(self.path_mapping.filename_display_for_diagnostics) + filename.display(RemapPathScopeComponents::DIAGNOSTICS) } pub fn is_multiline(&self, sp: Span) -> bool { @@ -1045,10 +1051,8 @@ impl SourceMap { } pub fn get_source_file(&self, filename: &FileName) -> Option> { - // Remap filename before lookup - let filename = self.path_mapping().map_filename_prefix(filename).0; for sf in self.files.borrow().source_files.iter() { - if filename == sf.name { + if *filename == sf.name { return Some(Arc::clone(&sf)); } } @@ -1080,16 +1084,20 @@ impl SourceMap { return None; }; - let local_path: Cow<'_, Path> = match name { - RealFileName::LocalPath(local_path) => local_path.into(), - RealFileName::Remapped { local_path: Some(local_path), .. } => local_path.into(), - RealFileName::Remapped { local_path: None, virtual_name } => { + let local_path: Cow<'_, Path> = match name.local_path() { + Some(local) => local.into(), + None => { // The compiler produces better error messages if the sources of dependencies // are available. Attempt to undo any path mapping so we can find remapped // dependencies. + // // We can only use the heuristic because `add_external_src` checks the file // content hash. - self.path_mapping.reverse_map_prefix_heuristically(virtual_name)?.into() + let maybe_remapped_path = name.path(RemapPathScopeComponents::DIAGNOSTICS); + self.path_mapping + .reverse_map_prefix_heuristically(maybe_remapped_path) + .map(Cow::from) + .unwrap_or(maybe_remapped_path.into()) } }; @@ -1135,35 +1143,25 @@ pub fn get_source_map() -> Option> { #[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>, bool) { + fn map_prefix<'a>(&'a self, path: impl Into>) -> (Cow<'a, Path>, bool) { let path = path.into(); if path.as_os_str().is_empty() { // Exit early if the path is empty and therefore there's nothing to remap. @@ -1209,138 +1207,68 @@ impl FilePathMapping { } } - fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) { - match file { - FileName::Real(realfile) if let RealFileName::LocalPath(local_path) = realfile => { - let (mapped_path, mapped) = self.map_prefix(local_path); - let realfile = if mapped { - RealFileName::Remapped { - local_path: Some(local_path.clone()), - virtual_name: mapped_path.into_owned(), - } - } else { - realfile.clone() - }; - (FileName::Real(realfile), mapped) - } - FileName::Real(_) => unreachable!("attempted to remap an already remapped filename"), - other => (other.clone(), false), - } - } - /// Applies any path prefix substitution as defined by the mapping. - /// The return value is the local path with a "virtual path" representing the remapped - /// part if any remapping was performed. - pub fn to_real_filename<'a>(&self, local_path: impl Into>) -> 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>, ) -> 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() + }, } } diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index c919aacf6b5f..16d28f393d7f 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -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; diff --git a/tests/run-make/duplicate-dependency/rmake.rs b/tests/run-make/duplicate-dependency/rmake.rs index 762d97e4311f..274db933feb8 100644 --- a/tests/run-make/duplicate-dependency/rmake.rs +++ b/tests/run-make/duplicate-dependency/rmake.rs @@ -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(); diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr index e77c0e5f68d6..86c1140573e3 100644 --- a/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr +++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-diag-in-deps.stderr @@ -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`