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
@ -17,7 +17,7 @@
|
||||
use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock};
|
||||
use std::cmp;
|
||||
use std::hash::Hash;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::{self, Path, PathBuf};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use std::fs;
|
||||
@ -1071,12 +1071,24 @@ pub fn count_lines(&self) -> usize {
|
||||
|
||||
pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
|
||||
source_file.add_external_src(|| {
|
||||
match source_file.name {
|
||||
FileName::Real(ref name) if let Some(local_path) = name.local_path() => {
|
||||
self.file_loader.read_file(local_path).ok()
|
||||
let FileName::Real(ref name) = source_file.name else {
|
||||
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 } => {
|
||||
// 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()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
self.file_loader.read_file(&local_path).ok()
|
||||
})
|
||||
}
|
||||
|
||||
@ -1277,4 +1289,43 @@ pub fn to_embeddable_absolute_path(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
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]
|
||||
fn path_prefix_remapping() {
|
||||
// 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]
|
||||
fn test_next_point() {
|
||||
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
|
||||
|
||||
// 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() {
|
||||
// We cannot actually put an ERROR marker here because
|
||||
// the file name in the error message is not what the
|
@ -1,5 +1,5 @@
|
||||
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
|
||||
| ^^^^^^ not found in this scope
|
Loading…
Reference in New Issue
Block a user