Heuristically undo path prefix mappings.
Because the compiler produces better diagnostics if it can find the source of (potentially remapped) dependencies.
This commit is contained in:
parent
44a500c8c1
commit
869df76764
8 changed files with 162 additions and 7 deletions
|
@ -17,7 +17,7 @@ use rustc_data_structures::stable_hasher::StableHasher;
|
||||||
use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock};
|
use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{self, Path, PathBuf};
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
@ -1071,12 +1071,24 @@ impl SourceMap {
|
||||||
|
|
||||||
pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
|
pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
|
||||||
source_file.add_external_src(|| {
|
source_file.add_external_src(|| {
|
||||||
match source_file.name {
|
let FileName::Real(ref name) = source_file.name else {
|
||||||
FileName::Real(ref name) if let Some(local_path) = name.local_path() => {
|
return None;
|
||||||
self.file_loader.read_file(local_path).ok()
|
};
|
||||||
}
|
|
||||||
_ => 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 } => {
|
||||||
|
// 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()
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.file_loader.read_file(&local_path).ok()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1277,4 +1289,43 @@ impl FilePathMapping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to (heuristically) reverse a prefix mapping.
|
||||||
|
///
|
||||||
|
/// Returns [`Some`] if there is exactly one mapping where the "to" part is
|
||||||
|
/// a prefix of `path` and has at least one non-empty
|
||||||
|
/// [`Normal`](path::Component::Normal) component. The component
|
||||||
|
/// restriction exists to avoid reverse mapping overly generic paths like
|
||||||
|
/// `/` or `.`).
|
||||||
|
///
|
||||||
|
/// This is a heuristic and not guaranteed to return the actual original
|
||||||
|
/// path! Do not rely on the result unless you have other means to verify
|
||||||
|
/// that the mapping is correct (e.g. by checking the file content hash).
|
||||||
|
#[instrument(level = "debug", skip(self), ret)]
|
||||||
|
fn reverse_map_prefix_heuristically(&self, path: &Path) -> Option<PathBuf> {
|
||||||
|
let mut found = None;
|
||||||
|
|
||||||
|
for (from, to) in self.mapping.iter() {
|
||||||
|
let has_normal_component = to.components().any(|c| match c {
|
||||||
|
path::Component::Normal(s) => !s.is_empty(),
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if !has_normal_component {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Ok(rest) = path.strip_prefix(to) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if found.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = Some(from.join(rest));
|
||||||
|
}
|
||||||
|
|
||||||
|
found
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,6 +344,10 @@ fn map_path_prefix(mapping: &FilePathMapping, p: &str) -> String {
|
||||||
mapping.map_prefix(path(p)).0.to_string_lossy().to_string()
|
mapping.map_prefix(path(p)).0.to_string_lossy().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reverse_map_prefix(mapping: &FilePathMapping, p: &str) -> Option<String> {
|
||||||
|
mapping.reverse_map_prefix_heuristically(&path(p)).map(|q| q.to_string_lossy().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_prefix_remapping() {
|
fn path_prefix_remapping() {
|
||||||
// Relative to relative
|
// Relative to relative
|
||||||
|
@ -480,6 +484,45 @@ fn path_prefix_remapping_expand_to_absolute() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_prefix_remapping_reverse() {
|
||||||
|
// Ignores options without alphanumeric chars.
|
||||||
|
{
|
||||||
|
let mapping =
|
||||||
|
&FilePathMapping::new(vec![(path("abc"), path("/")), (path("def"), path("."))]);
|
||||||
|
|
||||||
|
assert_eq!(reverse_map_prefix(mapping, "/hello.rs"), None);
|
||||||
|
assert_eq!(reverse_map_prefix(mapping, "./hello.rs"), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns `None` if multiple options match.
|
||||||
|
{
|
||||||
|
let mapping = &FilePathMapping::new(vec![
|
||||||
|
(path("abc"), path("/redacted")),
|
||||||
|
(path("def"), path("/redacted")),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(reverse_map_prefix(mapping, "/redacted/hello.rs"), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distinct reverse mappings.
|
||||||
|
{
|
||||||
|
let mapping = &FilePathMapping::new(vec![
|
||||||
|
(path("abc"), path("/redacted")),
|
||||||
|
(path("def/ghi"), path("/fake/dir")),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
reverse_map_prefix(mapping, "/redacted/path/hello.rs"),
|
||||||
|
Some(path_str("abc/path/hello.rs"))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
reverse_map_prefix(mapping, "/fake/dir/hello.rs"),
|
||||||
|
Some(path_str("def/ghi/hello.rs"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_next_point() {
|
fn test_next_point() {
|
||||||
let sm = SourceMap::new(FilePathMapping::empty());
|
let sm = SourceMap::new(FilePathMapping::empty());
|
||||||
|
|
3
tests/ui/errors/auxiliary/remapped_dep.rs
Normal file
3
tests/ui/errors/auxiliary/remapped_dep.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux
|
||||||
|
|
||||||
|
pub struct SomeStruct {} // This line should be show as part of the error.
|
14
tests/ui/errors/remap-path-prefix-reverse.local-self.stderr
Normal file
14
tests/ui/errors/remap-path-prefix-reverse.local-self.stderr
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
error[E0423]: expected value, found struct `remapped_dep::SomeStruct`
|
||||||
|
--> $DIR/remap-path-prefix-reverse.rs:22:13
|
||||||
|
|
|
||||||
|
LL | let _ = remapped_dep::SomeStruct;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}`
|
||||||
|
|
|
||||||
|
::: remapped-aux/remapped_dep.rs:3:1
|
||||||
|
|
|
||||||
|
LL | pub struct SomeStruct {} // This line should be show as part of the error.
|
||||||
|
| --------------------- `remapped_dep::SomeStruct` defined here
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0423`.
|
|
@ -0,0 +1,14 @@
|
||||||
|
error[E0423]: expected value, found struct `remapped_dep::SomeStruct`
|
||||||
|
--> remapped/errors/remap-path-prefix-reverse.rs:22:13
|
||||||
|
|
|
||||||
|
LL | let _ = remapped_dep::SomeStruct;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}`
|
||||||
|
|
|
||||||
|
::: remapped-aux/remapped_dep.rs:3:1
|
||||||
|
|
|
||||||
|
LL | pub struct SomeStruct {} // This line should be show as part of the error.
|
||||||
|
| --------------------- `remapped_dep::SomeStruct` defined here
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0423`.
|
23
tests/ui/errors/remap-path-prefix-reverse.rs
Normal file
23
tests/ui/errors/remap-path-prefix-reverse.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// aux-build:remapped_dep.rs
|
||||||
|
// compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux
|
||||||
|
|
||||||
|
// The remapped paths are not normalized by compiletest.
|
||||||
|
// normalize-stderr-test: "\\(errors)" -> "/$1"
|
||||||
|
|
||||||
|
// revisions: local-self remapped-self
|
||||||
|
// [remapped-self]compile-flags: --remap-path-prefix={{src-base}}=remapped
|
||||||
|
|
||||||
|
// The paths from `remapped-self` aren't recognized by compiletest, so we
|
||||||
|
// cannot use line-specific patterns for the actual error.
|
||||||
|
// error-pattern: E0423
|
||||||
|
|
||||||
|
// Verify that the expected source code is shown.
|
||||||
|
// error-pattern: pub struct SomeStruct {} // This line should be show
|
||||||
|
|
||||||
|
extern crate remapped_dep;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// The actual error is irrelevant. The important part it that is should show
|
||||||
|
// a snippet of the dependency's source.
|
||||||
|
let _ = remapped_dep::SomeStruct;
|
||||||
|
}
|
|
@ -1,5 +1,12 @@
|
||||||
// compile-flags: --remap-path-prefix={{src-base}}=remapped
|
// compile-flags: --remap-path-prefix={{src-base}}=remapped
|
||||||
|
|
||||||
|
// The remapped paths are not normalized by compiletest.
|
||||||
|
// normalize-stderr-test: "\\(errors)" -> "/$1"
|
||||||
|
|
||||||
|
// The remapped paths aren't recognized by compiletest, so we
|
||||||
|
// cannot use line-specific patterns.
|
||||||
|
// error-pattern: E0425
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// We cannot actually put an ERROR marker here because
|
// We cannot actually put an ERROR marker here because
|
||||||
// the file name in the error message is not what the
|
// the file name in the error message is not what the
|
|
@ -1,5 +1,5 @@
|
||||||
error[E0425]: cannot find value `ferris` in this scope
|
error[E0425]: cannot find value `ferris` in this scope
|
||||||
--> remapped/remap-path-prefix.rs:8:5
|
--> remapped/errors/remap-path-prefix.rs:15:5
|
||||||
|
|
|
|
||||||
LL | ferris
|
LL | ferris
|
||||||
| ^^^^^^ not found in this scope
|
| ^^^^^^ not found in this scope
|
Loading…
Add table
Add a link
Reference in a new issue