diff --git a/Cargo.lock b/Cargo.lock index 03a4d71f67c3a..ad3d06da1dd2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4632,7 +4632,6 @@ dependencies = [ name = "rustc_session" version = "0.0.0" dependencies = [ - "bitflags", "getopts", "libc", "rand 0.9.2", @@ -4659,6 +4658,7 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ + "bitflags", "blake3", "derive-where", "indexmap", diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index a9ab30fd8fa36..68f822aac403a 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -80,7 +80,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let OutlivesConstraint { sup, sub, locations, category, span, .. } = constraint; let (name, arg) = match locations { Locations::All(span) => { - ("All", tcx.sess.source_map().span_to_embeddable_string(*span)) + ("All", tcx.sess.source_map().span_to_diagnostic_string(*span)) } Locations::Single(loc) => ("Single", format!("{loc:?}")), }; diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 16adaab15c525..d49f4ded010bb 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 f31ad4f591b1e..6f14385d5d30d 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_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index db58ee8909113..12b0d5ec4963b 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -6,7 +6,10 @@ use std::path::{Component, Path}; use cranelift_codegen::MachSrcLoc; use cranelift_codegen::binemit::CodeOffset; use gimli::write::{FileId, FileInfo, LineProgram, LineString, LineStringTable}; -use rustc_span::{FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHashAlgorithm, hygiene}; +use rustc_span::{ + FileName, Pos, RemapPathScopeComponents, SourceFile, SourceFileAndLine, + SourceFileHashAlgorithm, hygiene, +}; use crate::debuginfo::FunctionDebugContext; use crate::debuginfo::emit::address_for_func; @@ -95,7 +98,7 @@ impl DebugContext { match &source_file.name { FileName::Real(path) => { let (dir_path, file_name) = - split_path_dir_and_file(path.to_path(self.filename_display_preference)); + split_path_dir_and_file(path.path(RemapPathScopeComponents::DEBUGINFO)); let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); let file_name = osstr_as_utf8_bytes(file_name); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 8c43db92fe05f..0cd510037293d 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -21,7 +21,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_session::Session; use rustc_session::config::DebugInfo; -use rustc_span::{FileNameDisplayPreference, SourceFileHash, StableSourceFileId}; +use rustc_span::{RemapPathScopeComponents, SourceFileHash, StableSourceFileId}; use rustc_target::callconv::FnAbi; pub(crate) use self::emit::{DebugReloc, DebugRelocName}; @@ -44,7 +44,6 @@ pub(crate) struct DebugContext { namespace_map: DefIdMap, array_size_type: Option, - filename_display_preference: FileNameDisplayPreference, embed_source: bool, } @@ -102,18 +101,18 @@ impl DebugContext { let mut dwarf = DwarfUnit::new(encoding); - use rustc_session::config::RemapPathScopeComponents; - - let filename_display_preference = - tcx.sess.filename_display_preference(RemapPathScopeComponents::DEBUGINFO); - let producer = producer(tcx.sess); - let comp_dir = - tcx.sess.opts.working_dir.to_string_lossy(filename_display_preference).to_string(); + let comp_dir = tcx + .sess + .source_map() + .working_dir() + .path(RemapPathScopeComponents::DEBUGINFO) + .to_string_lossy(); let (name, file_info) = match tcx.sess.local_crate_source_file() { Some(path) => { - let name = path.to_string_lossy(filename_display_preference).to_string(); + let name = + path.path(RemapPathScopeComponents::DEBUGINFO).to_string_lossy().into_owned(); (name, None) } None => (tcx.crate_name(LOCAL_CRATE).to_string(), None), @@ -137,7 +136,7 @@ impl DebugContext { { let name = dwarf.strings.add(format!("{name}/@/{cgu_name}")); - let comp_dir = dwarf.strings.add(comp_dir); + let comp_dir = dwarf.strings.add(&*comp_dir); let root = dwarf.unit.root(); let root = dwarf.unit.get_mut(root); @@ -180,7 +179,6 @@ impl DebugContext { stack_pointer_register, namespace_map: DefIdMap::default(), array_size_type, - filename_display_preference, embed_source, }) } diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 3979f62987f2c..53d3670c15248 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -297,29 +297,11 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let pos = span.lo(); let DebugLoc { file, line, col } = self.lookup_debug_loc(pos); match file.name { - rustc_span::FileName::Real(ref name) => match *name { - rustc_span::RealFileName::LocalPath(ref name) => { - if let Some(name) = name.to_str() { - self.context.new_location(name, line as i32, col as i32) - } else { - Location::null() - } - } - rustc_span::RealFileName::Remapped { - ref local_path, - virtual_name: ref _unused, - } => { - if let Some(name) = local_path.as_ref() { - if let Some(name) = name.to_str() { - self.context.new_location(name, line as i32, col as i32) - } else { - Location::null() - } - } else { - Location::null() - } - } - }, + rustc_span::FileName::Real(ref name) => self.context.new_location( + name.path(rustc_span::RemapPathScopeComponents::DEBUGINFO).to_string_lossy(), + line as i32, + col as i32, + ), _ => Location::null(), } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b131de1df8ba3..2c4943e835a66 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 b3a11f8b12bfc..bc54657a380a4 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 5535ebe65859e..dc941cb41c562 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); - - if filename_display_preference == FileNameDisplayPreference::Remapped { - let filename = cx - .sess() - .source_map() - .path_mapping() - .to_embeddable_absolute_path(filename.clone(), working_directory); - - // 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()) - } + let (working_directory, embeddable_name) = + filename.embeddable_name(RemapPathScopeComponents::DEBUGINFO); + + debug!(?working_directory, ?embeddable_name); + + 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 fccb6b171b1c2..0c677b34df7bf 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 1604b704033bd..962d035db862f 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 ce5c830bbfcd6..3b8c8baa5eff8 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 79bb5054dfeff..30be06ae0bd3b 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 20fb321307ac5..7f9b7c17be481 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_expand/src/stats.rs b/compiler/rustc_expand/src/stats.rs index 3e40632275b6f..00f1c11044e03 100644 --- a/compiler/rustc_expand/src/stats.rs +++ b/compiler/rustc_expand/src/stats.rs @@ -138,10 +138,7 @@ pub(crate) fn update_macro_stats( if false { let name = ExpnKind::Macro(macro_kind, name).descr(); let crate_name = &ecx.ecfg.crate_name; - let span = ecx - .sess - .source_map() - .span_to_string(span, rustc_span::FileNameDisplayPreference::Local); + let span = ecx.sess.source_map().span_to_diagnostic_string(span); eprint!( "\ -------------------------------\n\ diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ddfec9f886a6a..9acb69f5940db 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 bf7818b499331..9802caf705aa2 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; @@ -1654,15 +1654,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(), ); @@ -1671,17 +1671,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), + ); } } }; @@ -1696,15 +1691,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, @@ -1712,10 +1704,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 94bba94456102..165d6f1193d0d 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 6985cc7ddcfa1..fe352df3b9f04 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/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 3bdc5fdb420c2..b225dd4ef60a0 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -504,7 +504,7 @@ fn write_scope_tree( "{0:1$} // at {2}", indented_header, ALIGN, - tcx.sess.source_map().span_to_embeddable_string(span), + tcx.sess.source_map().span_to_diagnostic_string(span), )?; } else { writeln!(w, "{indented_header}")?; @@ -688,7 +688,7 @@ fn write_user_type_annotations( "| {:?}: user_ty: {}, span: {}, inferred_ty: {}", index.index(), annotation.user_ty, - tcx.sess.source_map().span_to_embeddable_string(annotation.span), + tcx.sess.source_map().span_to_diagnostic_string(annotation.span), with_no_trimmed_paths!(format!("{}", annotation.inferred_ty)), )?; } @@ -1420,7 +1420,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { self.push("mir::ConstOperand"); self.push(&format!( "+ span: {}", - self.tcx.sess.source_map().span_to_embeddable_string(*span) + self.tcx.sess.source_map().span_to_diagnostic_string(*span) )); if let Some(user_ty) = user_ty { self.push(&format!("+ user_ty: {user_ty:?}")); @@ -1503,7 +1503,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { } fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { - let location = tcx.sess.source_map().span_to_embeddable_string(span); + let location = tcx.sess.source_map().span_to_diagnostic_string(span); format!("scope {} at {}", scope.index(), location,) } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 6a4122ad67176..644916d556a73 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_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index deb4ab433bfe9..6fcb18177a142 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs @@ -49,7 +49,7 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In .unwrap_or_else(|e| format!("Failed {e:?}")); let closure_span = tcx.def_span(closure_def_id); - let src_file = tcx.sess.source_map().span_to_filename(closure_span); + let src_file = tcx.sess.source_map().span_to_diagnostic_string(closure_span); let line_nos = tcx .sess .source_map() @@ -57,14 +57,7 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In .map(|l| format!("{:?} {:?}", l.lines.first(), l.lines.last())) .unwrap_or_else(|e| format!("{e:?}")); - if let Err(e) = writeln!( - file, - "{}, {}, {}, {:?}", - old_size, - new_size, - src_file.prefer_local(), - line_nos - ) { + if let Err(e) = writeln!(file, "{}, {}, {}, {:?}", old_size, new_size, src_file, line_nos) { eprintln!("Error writing to file {e}") } } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 0992bee32f326..df8f970e05990 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 9b157cb6c7bf9..a46fcd30fef40 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 8ed2c2a892660..c02a01c1b8235 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 26728a34b84a5..56aa22378072a 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 6a98ae0663048..af6328d04d8bb 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, Span}; +use rustc_span::{BytePos, 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 0516982aeabb9..aebac3880d2fd 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 9b19961fcee4e..bda3f6d21b19e 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, RealFileName, SourceFileHashAlgorithm, Symbol, sym}; use rustc_target::spec::{ FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo, Target, TargetTuple, @@ -1022,9 +1019,15 @@ impl Input { "rust_out" } - pub fn source_name(&self) -> FileName { + pub fn file_name(&self, session: &Session) -> FileName { match *self { - Input::File(ref ifile) => ifile.clone().into(), + Input::File(ref ifile) => FileName::Real( + session + .psess + .source_map() + .path_mapping() + .to_real_filename(session.psess.source_map().working_dir(), ifile.as_path()), + ), Input::Str { ref name, .. } => name.clone(), } } @@ -1312,25 +1315,6 @@ impl OutputFilenames { } } -bitflags::bitflags! { - /// Scopes used to determined if it need to apply to --remap-path-prefix - #[derive(Clone, Copy, PartialEq, Eq, Hash)] - pub struct RemapPathScopeComponents: u8 { - /// Apply remappings to the expansion of std::file!() macro - const MACRO = 1 << 0; - /// Apply remappings to printed compiler diagnostics - const DIAGNOSTICS = 1 << 1; - /// Apply remappings to debug information - const DEBUGINFO = 1 << 3; - /// Apply remappings to coverage information - const COVERAGE = 1 << 4; - - /// An alias for `macro`, `debuginfo` and `coverage`. This ensures all paths in compiled - /// executables, libraries and objects are remapped but not elsewhere. - const OBJECT = Self::MACRO.bits() | Self::DEBUGINFO.bits() | Self::COVERAGE.bits(); - } -} - #[derive(Clone, Debug)] pub struct Sysroot { pub explicit: Option, @@ -1369,25 +1353,22 @@ fn file_path_mapping( remap_path_prefix: Vec<(PathBuf, PathBuf)>, unstable_opts: &UnstableOptions, ) -> FilePathMapping { - FilePathMapping::new( - remap_path_prefix.clone(), - if unstable_opts.remap_path_scope.contains(RemapPathScopeComponents::DIAGNOSTICS) - && !remap_path_prefix.is_empty() - { - FileNameDisplayPreference::Remapped - } else { - FileNameDisplayPreference::Local - }, - if unstable_opts.remap_path_scope.is_all() { - FileNameEmbeddablePreference::RemappedOnly - } else { - FileNameEmbeddablePreference::LocalAndRemapped - }, - ) + FilePathMapping::new(remap_path_prefix.clone(), unstable_opts.remap_path_scope) } impl Default for Options { fn default() -> Options { + let unstable_opts = UnstableOptions::default(); + + // FIXME(Urgau): This is a hack that ideally shouldn't exist, but rustdoc + // currently uses this `Default` implementation, so we have no choice but + // to create a default working directory. + let working_dir = { + let working_dir = std::env::current_dir().unwrap(); + let file_mapping = file_path_mapping(Vec::new(), &unstable_opts); + file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) + }; + Options { assert_incr_state: None, crate_types: Vec::new(), @@ -1404,7 +1385,7 @@ impl Default for Options { test: false, incremental: None, untracked_state_hash: Default::default(), - unstable_opts: Default::default(), + unstable_opts, prints: Vec::new(), cg: Default::default(), error_format: ErrorOutputType::default(), @@ -1428,7 +1409,7 @@ impl Default for Options { json_unused_externs: JsonUnusedExterns::No, json_future_incompat: false, pretty: None, - working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()), + working_dir, color: ColorConfig::Auto, logical_env: FxIndexMap::default(), verbose: false, @@ -2782,12 +2763,16 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M .collect() }; - let working_dir = std::env::current_dir().unwrap_or_else(|e| { - early_dcx.early_fatal(format!("Current directory is invalid: {e}")); - }); + // Ideally we would use `SourceMap::working_dir` instead, but we don't have access to it + // so we manually create the potentially-remapped working directory + let working_dir = { + let working_dir = std::env::current_dir().unwrap_or_else(|e| { + early_dcx.early_fatal(format!("Current directory is invalid: {e}")); + }); - let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); - let working_dir = file_mapping.to_real_filename(&working_dir); + let file_mapping = file_path_mapping(remap_path_prefix.clone(), &unstable_opts); + file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) + }; let verbose = matches.opt_present("verbose") || unstable_opts.verbose_internals; @@ -3115,8 +3100,8 @@ pub(crate) mod dep_tracking { use rustc_errors::LanguageIdentifier; use rustc_feature::UnstableFeatures; use rustc_hashes::Hash64; - use rustc_span::RealFileName; use rustc_span::edition::Edition; + use rustc_span::{RealFileName, RemapPathScopeComponents}; use rustc_target::spec::{ CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTuple, @@ -3129,8 +3114,8 @@ pub(crate) mod dep_tracking { FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, - RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, - SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + 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 d9792142a884d..7472f4f20e317 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::{RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm}; use rustc_target::spec::{ CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, @@ -492,7 +492,9 @@ top_level_options!( pretty: Option [UNTRACKED], /// The (potentially remapped) working directory + #[rustc_lint_opt_deny_field_access("use `SourceMap::working_dir` instead of this field")] working_dir: RealFileName [TRACKED], + color: ColorConfig [UNTRACKED], verbose: bool [TRACKED_NO_CRATE_HASH], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index acc65fc11a2a2..14b80099bafe4 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 43a2d692577eb..bf0cad63e5432 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 6a359c5f4656c..49b2e0c1ff1a7 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 associated path for the given remapping scope. + /// + /// ## 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() + } + } + + /// 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. - /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + /// + /// May not exists if the filename was imported from another crate. pub fn local_path(&self) -> Option<&Path> { - match self { - RealFileName::LocalPath(p) => Some(p), - RealFileName::Remapped { local_path, virtual_name: _ } => local_path.as_deref(), - } + 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. - /// Avoid embedding this in build artifacts; see `remapped_path_if_available()` for that. + /// + /// May not exists if the filename was imported from another crate. pub fn into_local_path(self) -> Option { - match self { - RealFileName::LocalPath(p) => Some(p), - RealFileName::Remapped { local_path: p, virtual_name: _ } => p, - } + self.local.map(|lp| lp.name) } - /// 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 whenever the filename was remapped. + pub(crate) fn was_remapped(&self) -> bool { + !self.scopes.is_empty() } - /// 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, + /// 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(), } } - /// Return the path remapped or not depending on the [`FileNameDisplayPreference`]. + /// Returns a `RealFileName` that is completely remapped without any local components. /// - /// 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(), + /// 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; } } - pub fn to_string_lossy(&self, display_pref: FileNameDisplayPreference) -> Cow<'_, str> { + /// 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,38 +467,18 @@ 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, +pub struct FileNameDisplay<'a> { + inner: &'a FileName, + display_pref: FileNameDisplayPreference, } -#[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. +// Internal enum. Should not be exposed. +#[derive(Clone, Copy)] +enum FileNameDisplayPreference { 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, + Scope(RemapPathScopeComponents), } impl fmt::Display for FileNameDisplay<'_> { @@ -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 c5a8bd3b15be9..ab47e441cc35a 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 17de34c8436f1..35694be9e492b 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -105,6 +105,9 @@ pub trait FileLoader { /// Read the contents of a potentially non-UTF-8 file into memory. /// We don't normalize binary files, so we can start in an Arc. fn read_binary_file(&self, path: &Path) -> io::Result>; + + /// Current working directory + fn current_directory(&self) -> io::Result; } /// A FileLoader that uses std::fs to load real files. @@ -170,6 +173,10 @@ impl FileLoader for RealFileLoader { file.read_to_end(&mut bytes)?; Ok(bytes.into()) } + + fn current_directory(&self) -> io::Result { + std::env::current_dir() + } } // _____________________________________________________________________________ @@ -198,6 +205,9 @@ pub struct SourceMap { // `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`. path_mapping: FilePathMapping, + /// Current working directory + working_dir: RealFileName, + /// The algorithm used for hashing the contents of each source file. hash_kind: SourceFileHashAlgorithm, @@ -221,8 +231,14 @@ impl SourceMap { pub fn with_inputs( SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }: SourceMapInputs, ) -> SourceMap { + let cwd = file_loader + .current_directory() + .expect("expecting a current working directory to exist"); + let working_dir = path_mapping.to_real_filename(&RealFileName::empty(), &cwd); + debug!(?working_dir); SourceMap { files: Default::default(), + working_dir, file_loader: IntoDynSyncSend(file_loader), path_mapping, hash_kind, @@ -234,13 +250,17 @@ impl SourceMap { &self.path_mapping } + pub fn working_dir(&self) -> &RealFileName { + &self.working_dir + } + pub fn file_exists(&self, path: &Path) -> bool { self.file_loader.file_exists(path) } pub fn load_file(&self, path: &Path) -> io::Result> { let src = self.file_loader.read_file(path)?; - let filename = path.to_owned().into(); + let filename = FileName::Real(self.path_mapping.to_real_filename(&self.working_dir, path)); Ok(self.new_source_file(filename, src)) } @@ -257,7 +277,8 @@ impl SourceMap { // via `mod`, so we try to use real file contents and not just an // empty string. let text = std::str::from_utf8(&bytes).unwrap_or("").to_string(); - let file = self.new_source_file(path.to_owned().into(), text); + let filename = FileName::Real(self.path_mapping.to_real_filename(&self.working_dir, path)); + let file = self.new_source_file(filename, text); Ok(( bytes, Span::new( @@ -325,7 +346,6 @@ impl SourceMap { // Note that filename may not be a valid path, eg it may be `` etc, // but this is okay because the directory determined by `path.pop()` will // be empty, so the working directory will be used. - let (filename, _) = self.path_mapping.map_filename_prefix(&filename); let stable_id = StableSourceFileId::from_filename_in_current_crate(&filename); match self.source_file_by_stable_id(stable_id) { @@ -424,25 +444,36 @@ impl SourceMap { } } - pub fn span_to_string( + pub fn span_to_string(&self, sp: Span, display_scope: RemapPathScopeComponents) -> String { + self.span_to_string_ext(sp, display_scope, false) + } + + pub fn span_to_short_string( &self, sp: Span, - filename_display_pref: FileNameDisplayPreference, + display_scope: RemapPathScopeComponents, + ) -> String { + self.span_to_string_ext(sp, display_scope, true) + } + + fn span_to_string_ext( + &self, + sp: Span, + display_scope: RemapPathScopeComponents, + short: bool, ) -> String { let (source_file, lo_line, lo_col, hi_line, hi_col) = self.span_to_location_info(sp); let file_name = match source_file { - Some(sf) => sf.name.display(filename_display_pref).to_string(), + Some(sf) => { + if short { sf.name.short() } else { sf.name.display(display_scope) }.to_string() + } None => return "no-location".to_string(), }; format!( "{file_name}:{lo_line}:{lo_col}{}", - if let FileNameDisplayPreference::Short = filename_display_pref { - String::new() - } else { - format!(": {hi_line}:{hi_col}") - } + if short { String::new() } else { format!(": {hi_line}:{hi_col}") } ) } @@ -459,16 +490,11 @@ impl SourceMap { (Some(lo.file), lo.line, lo.col.to_usize() + 1, hi.line, hi.col.to_usize() + 1) } - /// Format the span location suitable for embedding in build artifacts - pub fn span_to_embeddable_string(&self, sp: Span) -> String { - self.span_to_string(sp, FileNameDisplayPreference::Remapped) - } - /// Format the span location to be printed in diagnostics. Must not be emitted /// to build artifacts as this may leak local file paths. Use span_to_embeddable_string /// for string suitable for embedding. pub fn span_to_diagnostic_string(&self, sp: Span) -> String { - self.span_to_string(sp, self.path_mapping.filename_display_for_diagnostics) + self.span_to_string(sp, RemapPathScopeComponents::DIAGNOSTICS) } pub fn span_to_filename(&self, sp: Span) -> FileName { @@ -476,7 +502,7 @@ impl SourceMap { } pub fn filename_for_diagnostics<'a>(&self, filename: &'a FileName) -> FileNameDisplay<'a> { - filename.display(self.path_mapping.filename_display_for_diagnostics) + filename.display(RemapPathScopeComponents::DIAGNOSTICS) } pub fn is_multiline(&self, sp: Span) -> bool { @@ -1025,10 +1051,8 @@ impl SourceMap { } pub fn get_source_file(&self, filename: &FileName) -> Option> { - // Remap filename before lookup - let filename = self.path_mapping().map_filename_prefix(filename).0; for sf in self.files.borrow().source_files.iter() { - if filename == sf.name { + if *filename == sf.name { return Some(Arc::clone(&sf)); } } @@ -1060,16 +1084,20 @@ impl SourceMap { return None; }; - let local_path: Cow<'_, Path> = match name { - RealFileName::LocalPath(local_path) => local_path.into(), - RealFileName::Remapped { local_path: Some(local_path), .. } => local_path.into(), - RealFileName::Remapped { local_path: None, virtual_name } => { + let local_path: Cow<'_, Path> = match name.local_path() { + Some(local) => local.into(), + None => { // The compiler produces better error messages if the sources of dependencies // are available. Attempt to undo any path mapping so we can find remapped // dependencies. + // // We can only use the heuristic because `add_external_src` checks the file // content hash. - self.path_mapping.reverse_map_prefix_heuristically(virtual_name)?.into() + let maybe_remapped_path = name.path(RemapPathScopeComponents::DIAGNOSTICS); + self.path_mapping + .reverse_map_prefix_heuristically(maybe_remapped_path) + .map(Cow::from) + .unwrap_or(maybe_remapped_path.into()) } }; @@ -1115,35 +1143,25 @@ pub fn get_source_map() -> Option> { #[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. @@ -1189,138 +1207,68 @@ impl FilePathMapping { } } - fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) { - match file { - FileName::Real(realfile) if let RealFileName::LocalPath(local_path) = realfile => { - let (mapped_path, mapped) = self.map_prefix(local_path); - let realfile = if mapped { - RealFileName::Remapped { - local_path: Some(local_path.clone()), - virtual_name: mapped_path.into_owned(), - } - } else { - realfile.clone() - }; - (FileName::Real(realfile), mapped) - } - FileName::Real(_) => unreachable!("attempted to remap an already remapped filename"), - other => (other.clone(), false), - } - } - /// Applies any path prefix substitution as defined by the mapping. - /// The return value is the local path with a "virtual path" representing the remapped - /// part if any remapping was performed. - pub fn to_real_filename<'a>(&self, local_path: impl Into>) -> 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); + + // 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) + }, + }; - 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()); - } + 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(); - debug_assert!(new_path.is_relative()); - let unmapped_file_path_rel = new_path; - - match working_directory { - RealFileName::LocalPath(unmapped_working_dir_abs) => { - let unmapped_file_path_abs = - unmapped_working_dir_abs.join(unmapped_file_path_rel); - - // 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)), - }, - } - } - } - } + 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), + ); + + // 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 c919aacf6b5f4..16d28f393d7f2 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") } + 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") + }, + scopes: RemapPathScopeComponents::all() + } ); - // Unmapped absolute path with unrelated working directory + // 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") } + 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::all() + } ); - // Unmapped absolute path that does not match any prefix + // 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")), + 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() + } ); - // Unmapped relative path + // 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") } + 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() + } ); - // Unmapped relative path with `./` + // 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") } + 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() + } ); - // Unmapped relative path that does not match any prefix + // 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")), + mapping.to_real_filename( + &mapping.to_real_filename(&RealFileName::empty(), path("/abc")), + path("quux/src/main.rs"), ), - RealFileName::LocalPath(path("/abc/quux/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_with_mixed_scopes() { + // "virtual" working directory is relative path + let mapping = &FilePathMapping::new( + vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))], + RemapPathScopeComponents::OBJECT, ); + let working_directory = path("/foo"); + let working_directory = mapping.to_real_filename(&RealFileName::empty(), working_directory); - // Already remapped absolute path + 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")); + + // 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::OBJECT + } ); - // 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::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 + } ); - // Already remapped relative path + // Relative 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( + &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_without_remapped_cwd() { // "virtual" working directory is relative path let mapping = &FilePathMapping::new( - vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))], - FileNameDisplayPreference::Remapped, - FileNameEmbeddablePreference::LocalAndRemapped, + vec![(path("/foo"), path("FOO")), (path("/cwd/bar"), path("CWDBAR"))], + 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 = path("/cwd"); + 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("/cwd")); + assert_eq!(working_directory.path(RemapPathScopeComponents::MACRO), path("/cwd")); + assert_eq!(working_directory.path(RemapPathScopeComponents::DEBUGINFO), path("/cwd")); - // 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") + 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 } ); - // Unmapped absolute path with unrelated working directory + // Absolute path with unrelated root 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") + 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() } ); - // Already remapped absolute path, with unrelated working directory + // Absolute path with cwd 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("/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") }, - &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 + // Relative path assert_eq!( - mapping.to_embeddable_absolute_path( - RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") }, - &working_directory - ), - RealFileName::Remapped { local_path: None, virtual_name: path("XYZ/src/main.rs") } + 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/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f1b0f4a68beaa..1e01689b560a6 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -25,7 +25,7 @@ use rustc_resolve::rustdoc::{ use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, FileName, Loc}; +use rustc_span::{DUMMY_SP, FileName, Loc, RemapPathScopeComponents}; use tracing::{debug, trace}; use {rustc_ast as ast, rustc_hir as hir}; @@ -148,10 +148,17 @@ impl ExternalCrate { pub(crate) fn src_root(&self, tcx: TyCtxt<'_>) -> PathBuf { match self.src(tcx) { - FileName::Real(ref p) => match p.local_path_if_available().parent() { - Some(p) => p.to_path_buf(), - None => PathBuf::new(), - }, + FileName::Real(ref p) => { + match p + .local_path() + .or(Some(p.path(RemapPathScopeComponents::MACRO))) + .unwrap() + .parent() + { + Some(p) => p.to_path_buf(), + None => PathBuf::new(), + } + } _ => PathBuf::new(), } } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 481aa392007c8..1359b51697821 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -27,7 +27,7 @@ use rustc_session::config::{self, CrateType, ErrorOutputType, Input}; use rustc_session::lint; use rustc_span::edition::Edition; use rustc_span::symbol::sym; -use rustc_span::{FileName, Span}; +use rustc_span::{FileName, RemapPathScopeComponents, Span}; use rustc_target::spec::{Target, TargetTuple}; use tempfile::{Builder as TempFileBuilder, TempDir}; use tracing::debug; @@ -944,14 +944,7 @@ impl ScrapedDocTest { } fn path(&self) -> PathBuf { match &self.filename { - FileName::Real(path) => { - if let Some(local_path) = path.local_path() { - local_path.to_path_buf() - } else { - // Somehow we got the filename from the metadata of another crate, should never happen - unreachable!("doctest from a different crate"); - } - } + FileName::Real(name) => name.path(RemapPathScopeComponents::DIAGNOSTICS).to_path_buf(), _ => PathBuf::from(r"doctest.rs"), } } @@ -999,7 +992,7 @@ impl CreateRunnableDocTests { // For example `module/file.rs` would become `module_file_rs` let file = scraped_test .filename - .prefer_local() + .prefer_local_unconditionally() .to_string_lossy() .chars() .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }) diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs index 7f26605f2562c..eba2b0adae5c7 100644 --- a/src/librustdoc/doctest/markdown.rs +++ b/src/librustdoc/doctest/markdown.rs @@ -4,7 +4,8 @@ use std::fs::read_to_string; use std::sync::{Arc, Mutex}; use rustc_session::config::Input; -use rustc_span::{DUMMY_SP, FileName}; +use rustc_span::source_map::FilePathMapping; +use rustc_span::{DUMMY_SP, FileName, RealFileName}; use tempfile::tempdir; use super::{ @@ -105,8 +106,12 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> { cur_path: vec![], filename: input .opt_path() - .map(ToOwned::to_owned) - .map(FileName::from) + .map(|f| { + // We don't have access to a rustc Session so let's just use a dummy + // filepath mapping to create a real filename. + let file_mapping = FilePathMapping::empty(); + FileName::Real(file_mapping.to_real_filename(&RealFileName::empty(), f)) + }) .unwrap_or(FileName::Custom("input".to_owned())), }; let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 4d3f976c2a6d5..95080ec3bb72e 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -1,7 +1,6 @@ //! Doctest functionality used only for doctests in `.rs` source files. use std::cell::Cell; -use std::env; use std::sync::Arc; use rustc_ast_pretty::pprust; @@ -28,15 +27,6 @@ struct RustCollector { impl RustCollector { fn get_filename(&self) -> FileName { let filename = self.source_map.span_to_filename(self.position); - if let FileName::Real(ref filename) = filename { - let path = filename.remapped_path_if_available(); - // Strip the cwd prefix from the path. This will likely exist if - // the path was not remapped. - let path = env::current_dir() - .map(|cur_dir| path.strip_prefix(&cur_dir).unwrap_or(path)) - .unwrap_or(path); - return path.to_owned().into(); - } filename } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index e42997d5b4a14..501d2653e29b6 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -361,7 +361,7 @@ impl<'tcx> Context<'tcx> { // We can safely ignore synthetic `SourceFile`s. let file = match span.filename(self.sess()) { - FileName::Real(ref path) => path.local_path_if_available().to_path_buf(), + FileName::Real(ref path) => path.local_path()?.to_path_buf(), _ => return None, }; let file = &file; @@ -495,7 +495,7 @@ impl<'tcx> Context<'tcx> { } = options; let src_root = match krate.src(tcx) { - FileName::Real(ref p) => match p.local_path_if_available().parent() { + FileName::Real(ref p) => match p.local_path().map(|p| p.parent()).flatten() { Some(p) => p.to_path_buf(), None => PathBuf::new(), }, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8740b5935973c..4529f5a8c0163 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -60,7 +60,7 @@ use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, Stable use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{Symbol, sym}; -use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; +use rustc_span::{BytePos, DUMMY_SP, FileName}; use tracing::{debug, info}; pub(crate) use self::context::*; @@ -2772,7 +2772,7 @@ fn render_call_locations( files .iter() .find(|file| match &file.name { - FileName::Real(RealFileName::LocalPath(other_path)) => rel_path == other_path, + FileName::Real(real) => real.local_path().map_or(false, |p| p == rel_path), _ => false, }) .map(|file| file.start_pos) diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index f04f94432b808..25bd8b8c0de03 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::{FileName, FileNameDisplayPreference, RealFileName, sym}; +use rustc_span::{FileName, RealFileName, RemapPathScopeComponents, sym}; use tracing::info; use super::render::Context; @@ -148,7 +148,10 @@ impl DocVisitor<'_> for SourceCollector<'_, '_> { span, format!( "failed to render source code for `{filename}`: {e}", - filename = filename.to_string_lossy(FileNameDisplayPreference::Local), + filename = filename + .path(RemapPathScopeComponents::DIAGNOSTICS) + .to_string_lossy() + .into_owned(), ), ); false @@ -224,10 +227,7 @@ impl SourceCollector<'_, '_> { cur.push(&fname); let title = format!("{} - source", src_fname.to_string_lossy()); - let desc = format!( - "Source of the Rust file `{}`.", - file.to_string_lossy(FileNameDisplayPreference::Remapped) - ); + let desc = format!("Source of the Rust file `{}`.", p.to_string_lossy()); let page = layout::Page { title: &title, short_title: &src_fname.to_string_lossy(), diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 66d8b667a4cad..2782baae5e209 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -7,7 +7,7 @@ use rustc_hir as hir; use rustc_lint::builtin::MISSING_DOCS; use rustc_middle::lint::{LevelAndSource, LintLevelSource}; use rustc_session::lint; -use rustc_span::FileName; +use rustc_span::{FileName, RemapPathScopeComponents}; use serde::Serialize; use tracing::debug; @@ -124,7 +124,7 @@ impl CoverageCalculator<'_, '_> { &self .items .iter() - .map(|(k, v)| (k.prefer_local().to_string(), v)) + .map(|(k, v)| (k.prefer_local_unconditionally().to_string(), v)) .collect::>(), ) .expect("failed to convert JSON data to string") @@ -167,7 +167,11 @@ impl CoverageCalculator<'_, '_> { for (file, &count) in &self.items { if let Some(percentage) = count.percentage() { print_table_record( - &limit_filename_len(file.prefer_local().to_string_lossy().into()), + &limit_filename_len( + file.display(RemapPathScopeComponents::DIAGNOSTICS) + .to_string_lossy() + .into(), + ), count, percentage, count.examples_percentage().unwrap_or(0.), diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 9096d6f1c7b32..befd50a5c85fa 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -81,7 +81,7 @@ pub struct ModStyle { impl EarlyLintPass for ModStyle { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { - self.working_dir = cx.sess().opts.working_dir.local_path().map(Path::to_path_buf); + self.working_dir = cx.sess().source_map().working_dir().local_path().map(Path::to_path_buf); } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index e29d45551d1b0..b48d17863aa30 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - BytePos, DUMMY_SP, DesugaringKind, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, + BytePos, DUMMY_SP, DesugaringKind, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, hygiene, }; use std::borrow::Cow; @@ -268,7 +268,7 @@ fn map_range( debug_assert!( range.start <= text.len() && range.end <= text.len(), "Range `{range:?}` is outside the source file (file `{}`, length `{}`)", - src.sf.name.display(FileNameDisplayPreference::Local), + src.sf.name.prefer_local_unconditionally(), text.len(), ); debug_assert!(range.start <= range.end, "Range `{range:?}` has overlapping bounds"); diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 01f77f261d703..0c7ce472556b4 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -627,7 +627,7 @@ pub fn report_msg<'tcx>( err.subdiagnostic(frame_info.as_note(machine.tcx)); } else { let sm = sess.source_map(); - let span = sm.span_to_embeddable_string(frame_info.span); + let span = sm.span_to_diagnostic_string(frame_info.span); err.note(format!("{frame_info} at {span}")); } } diff --git a/src/tools/rustfmt/src/config/file_lines.rs b/src/tools/rustfmt/src/config/file_lines.rs index 2f2a6c8d55205..f9ae0d16e298d 100644 --- a/src/tools/rustfmt/src/config/file_lines.rs +++ b/src/tools/rustfmt/src/config/file_lines.rs @@ -28,7 +28,15 @@ pub enum FileName { impl From for FileName { fn from(name: rustc_span::FileName) -> FileName { match name { - rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(p)) => FileName::Real(p), + rustc_span::FileName::Real(real) => { + if let Some(p) = real.into_local_path() { + FileName::Real(p) + } else { + // rustfmt does not remap filenames; the local path should always + // remain accessible. + unreachable!() + } + } rustc_span::FileName::Custom(ref f) if f == "stdin" => FileName::Stdin, _ => unreachable!(), } diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 10e2809e58bf4..2fd8bfdaf3e11 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -58,19 +58,19 @@ impl Emitter for SilentOnIgnoredFilesEmitter { } if let Some(primary_span) = &diag.span.primary_span() { let file_name = self.source_map.span_to_filename(*primary_span); - if let rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(ref path)) = - file_name - { - if self - .ignore_path_set - .is_match(&FileName::Real(path.to_path_buf())) - { - if !self.has_non_ignorable_parser_errors { - self.can_reset.store(true, Ordering::Release); + if let rustc_span::FileName::Real(real) = file_name { + if let Some(path) = real.local_path() { + if self + .ignore_path_set + .is_match(&FileName::Real(path.to_path_buf())) + { + if !self.has_non_ignorable_parser_errors { + self.can_reset.store(true, Ordering::Release); + } + return; } - return; } - }; + } } self.handle_non_ignoreable_error(diag, registry); } @@ -181,7 +181,10 @@ impl ParseSess { self.raw_psess .source_map() .get_source_file(&rustc_span::FileName::Real( - rustc_span::RealFileName::LocalPath(path.to_path_buf()), + self.raw_psess + .source_map() + .path_mapping() + .to_real_filename(self.raw_psess.source_map().working_dir(), path), )) .is_some() } @@ -246,10 +249,20 @@ impl ParseSess { ) } - pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option> { + pub(crate) fn get_original_snippet(&self, filename: &FileName) -> Option> { + let rustc_filename = match filename { + FileName::Real(path) => rustc_span::FileName::Real( + self.raw_psess + .source_map() + .path_mapping() + .to_real_filename(self.raw_psess.source_map().working_dir(), path), + ), + FileName::Stdin => rustc_span::FileName::Custom("stdin".to_owned()), + }; + self.raw_psess .source_map() - .get_source_file(&file_name.into()) + .get_source_file(&rustc_filename) .and_then(|source_file| source_file.src.clone()) } } @@ -313,7 +326,7 @@ mod tests { use crate::config::IgnoreList; use crate::utils::mk_sp; use rustc_errors::MultiSpan; - use rustc_span::{FileName as SourceMapFileName, RealFileName}; + use rustc_span::FileName as SourceMapFileName; use std::path::PathBuf; use std::sync::atomic::AtomicU32; @@ -372,6 +385,13 @@ mod tests { .ignore() } + fn filename(sm: &SourceMap, path: &str) -> SourceMapFileName { + SourceMapFileName::Real( + sm.path_mapping() + .to_real_filename(sm.working_dir(), PathBuf::from(path)), + ) + } + #[test] fn handles_fatal_parse_error_in_ignored_file() { let num_emitted_errors = Arc::new(AtomicU32::new(0)); @@ -380,10 +400,7 @@ mod tests { let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); let source = String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), - source, - ); + source_map.new_source_file(filename(&source_map, "foo.rs"), source); let registry = Registry::new(&[]); let mut emitter = build_emitter( Arc::clone(&num_emitted_errors), @@ -406,10 +423,7 @@ mod tests { let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); let source = String::from(r#"pub fn bar() { 1x; }"#); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), - source, - ); + source_map.new_source_file(filename(&source_map, "foo.rs"), source); let registry = Registry::new(&[]); let mut emitter = build_emitter( Arc::clone(&num_emitted_errors), @@ -431,10 +445,7 @@ mod tests { let can_reset_errors = Arc::new(AtomicBool::new(false)); let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); let source = String::from(r#"pub fn bar() { 1x; }"#); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), - source, - ); + source_map.new_source_file(filename(&source_map, "foo.rs"), source); let registry = Registry::new(&[]); let mut emitter = build_emitter( Arc::clone(&num_emitted_errors), @@ -460,18 +471,9 @@ mod tests { let foo_source = String::from(r#"pub fn foo() { 1x; }"#); let fatal_source = String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("bar.rs"))), - bar_source, - ); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))), - foo_source, - ); - source_map.new_source_file( - SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))), - fatal_source, - ); + source_map.new_source_file(filename(&source_map, "bar.rs"), bar_source); + source_map.new_source_file(filename(&source_map, "foo.rs"), foo_source); + source_map.new_source_file(filename(&source_map, "fatal.rs"), fatal_source); let registry = Registry::new(&[]); let mut emitter = build_emitter( Arc::clone(&num_emitted_errors), diff --git a/src/tools/rustfmt/src/source_file.rs b/src/tools/rustfmt/src/source_file.rs index e942058a0a83e..b0ec24f3db663 100644 --- a/src/tools/rustfmt/src/source_file.rs +++ b/src/tools/rustfmt/src/source_file.rs @@ -65,18 +65,6 @@ where } } - #[allow(non_local_definitions)] - impl From<&FileName> for rustc_span::FileName { - fn from(filename: &FileName) -> rustc_span::FileName { - match filename { - FileName::Real(path) => { - rustc_span::FileName::Real(rustc_span::RealFileName::LocalPath(path.to_owned())) - } - FileName::Stdin => rustc_span::FileName::Custom("stdin".to_owned()), - } - } - } - // SourceFile's in the SourceMap will always have Unix-style line endings // See: https://github.com/rust-lang/rustfmt/issues/3850 // So if the user has explicitly overridden the rustfmt `newline_style` diff --git a/tests/run-make/duplicate-dependency/rmake.rs b/tests/run-make/duplicate-dependency/rmake.rs index 762d97e4311f4..274db933feb83 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/run-make/remap-path-prefix-consts/location-caller.rs b/tests/run-make/remap-path-prefix-consts/location-caller.rs new file mode 100644 index 0000000000000..70685e032e4af --- /dev/null +++ b/tests/run-make/remap-path-prefix-consts/location-caller.rs @@ -0,0 +1,21 @@ +// Reproducer from https://github.com/rust-lang/rust/issues/148328#issuecomment-3473688412 +#[inline(always)] +pub const fn the_path() -> &'static str { + std::panic::Location::caller().file() +} + +#[inline(never)] +pub fn the_path2() -> &'static str { + const { std::panic::Location::caller().file() } +} + +// Reproducer from https://github.com/rust-lang/rust/issues/148328#issuecomment-3473761194 +pub const fn the_path_len() -> usize { + std::panic::Location::caller().file().len() +} + +pub type Array = [u8; the_path_len()]; + +pub fn the_zeroed_path_len_array() -> Array { + [0; _] +} diff --git a/tests/run-make/remap-path-prefix-consts/rmake.rs b/tests/run-make/remap-path-prefix-consts/rmake.rs new file mode 100644 index 0000000000000..d07a5e00768a7 --- /dev/null +++ b/tests/run-make/remap-path-prefix-consts/rmake.rs @@ -0,0 +1,115 @@ +//@ ignore-cross-compile (relocations in generic ELF against `arm-unknown-linux-gnueabihf`) +//@ needs-target-std + +use run_make_support::{bin_name, cwd, run, rustc}; + +fn main() { + // No remapping - relative paths + { + let runner_bin = bin_name("runner-no-remap-rel-paths"); + + let mut location_caller = rustc(); + location_caller.crate_type("lib").input("location-caller.rs"); + location_caller.run(); + + let mut runner = rustc(); + runner.crate_type("bin").input("runner.rs").output(&runner_bin); + runner.run(); + + run(&runner_bin); + } + + // No remapping - absolute paths + { + let runner_bin = bin_name("runner-no-remap-abs-paths"); + + let mut location_caller = rustc(); + location_caller.crate_type("lib").input(cwd().join("location-caller.rs")); + location_caller.run(); + + let mut runner = rustc(); + runner.crate_type("bin").input(cwd().join("runner.rs")).output(&runner_bin); + runner.run(); + + run(&runner_bin); + } + + // No remapping - mixed paths + { + let runner_bin = bin_name("runner-no-remap-mixed-paths"); + + let mut location_caller = rustc(); + location_caller.crate_type("lib").input(cwd().join("location-caller.rs")); + location_caller.run(); + + let mut runner = rustc(); + runner.crate_type("bin").input("runner.rs").output(&runner_bin); + runner.run(); + + run(&runner_bin); + } + + // Remapping current working directory + { + let runner_bin = bin_name("runner-remap-cwd"); + + let mut location_caller = rustc(); + location_caller + .crate_type("lib") + .remap_path_prefix(cwd(), "/remapped") + .input(cwd().join("location-caller.rs")); + location_caller.run(); + + let mut runner = rustc(); + runner + .crate_type("bin") + .remap_path_prefix(cwd(), "/remapped") + .input(cwd().join("runner.rs")) + .output(&runner_bin); + runner.run(); + + run(&runner_bin); + } + + // Remapping current working directory - only in the dependency + { + let runner_bin = bin_name("runner-remap-cwd-only-dep"); + + let mut location_caller = rustc(); + location_caller + .crate_type("lib") + .remap_path_prefix(cwd(), "/remapped") + .input(cwd().join("location-caller.rs")); + location_caller.run(); + + let mut runner = rustc(); + runner.crate_type("bin").input(cwd().join("runner.rs")).output(&runner_bin); + runner.run(); + + run(&runner_bin); + } + + // Remapping current working directory - different scopes + { + let runner_bin = bin_name("runner-remap-cwd-diff-scope"); + + let mut location_caller = rustc(); + location_caller + .crate_type("lib") + .remap_path_prefix(cwd(), "/remapped") + .arg("-Zremap-path-scope=object") + .input(cwd().join("location-caller.rs")); + location_caller.run(); + + let mut runner = rustc(); + runner + .crate_type("bin") + .remap_path_prefix(cwd(), "/remapped") + .arg("-Zremap-path-scope=diagnostics") + .input(cwd().join("runner.rs")) + .output(&runner_bin); + runner.run(); + + run(&runner_bin); + } +} diff --git a/tests/run-make/remap-path-prefix-consts/runner.rs b/tests/run-make/remap-path-prefix-consts/runner.rs new file mode 100644 index 0000000000000..d67dbe3b398a9 --- /dev/null +++ b/tests/run-make/remap-path-prefix-consts/runner.rs @@ -0,0 +1,19 @@ +// Verifies that the paths are the same and consistent between this crate and location_caller crate. +// +// https://github.com/rust-lang/rust/issues/148328 + +extern crate location_caller; + +fn main() { + { + // Assert both paths are the same + let the_path = location_caller::the_path(); + let the_path2 = location_caller::the_path2(); + assert_eq!(the_path, the_path2); + } + + { + // Let's make sure we don't read OOB memory + println!("{:?}", location_caller::the_zeroed_path_len_array()); + } +} 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 e77c0e5f68d65..86c1140573e3c 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`