fix: prefix prelude items whose name collides in current scope

This commit is contained in:
Ryo Yoshida 2023-01-02 20:31:35 +09:00
parent 643bc02ded
commit cf2fa14db5
No known key found for this signature in database
GPG key ID: E25698A930586171

View file

@ -107,7 +107,7 @@ fn find_path_inner(
} }
// - if the item is in the prelude, return the name from there // - if the item is in the prelude, return the name from there
if let Some(value) = find_in_prelude(db, &crate_root.def_map(db), item, from) { if let value @ Some(_) = find_in_prelude(db, &crate_root.def_map(db), &def_map, item, from) {
return value; return value;
} }
@ -205,7 +205,8 @@ fn find_path_for_module(
} }
} }
if let Some(value) = find_in_prelude(db, &root_def_map, ItemInNs::Types(module_id.into()), from) if let value @ Some(_) =
find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from)
{ {
return value; return value;
} }
@ -234,24 +235,42 @@ fn find_in_scope(
}) })
} }
/// Returns single-segment path (i.e. without any prefix) if `item` is found in prelude and its
/// name doesn't clash in current scope.
fn find_in_prelude( fn find_in_prelude(
db: &dyn DefDatabase, db: &dyn DefDatabase,
root_def_map: &DefMap, root_def_map: &DefMap,
local_def_map: &DefMap,
item: ItemInNs, item: ItemInNs,
from: ModuleId, from: ModuleId,
) -> Option<Option<ModPath>> { ) -> Option<ModPath> {
if let Some(prelude_module) = root_def_map.prelude() { let prelude_module = root_def_map.prelude()?;
// Preludes in block DefMaps are ignored, only the crate DefMap is searched // Preludes in block DefMaps are ignored, only the crate DefMap is searched
let prelude_def_map = prelude_module.def_map(db); let prelude_def_map = prelude_module.def_map(db);
let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
if let Some((name, vis)) = prelude_scope.name_of(item) { let (name, vis) = prelude_scope.name_of(item)?;
if vis.is_visible_from(db, from) { if !vis.is_visible_from(db, from) {
return Some(Some(ModPath::from_segments(PathKind::Plain, Some(name.clone())))); return None;
}
}
} }
// Check if the name is in current scope and it points to the same def.
let found_and_same_def =
local_def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
let per_ns = def_map[local_id].scope.get(name);
let same_def = match item {
ItemInNs::Types(it) => per_ns.take_types()? == it,
ItemInNs::Values(it) => per_ns.take_values()? == it,
ItemInNs::Macros(it) => per_ns.take_macros()? == it,
};
Some(same_def)
});
if found_and_same_def.unwrap_or(true) {
Some(ModPath::from_segments(PathKind::Plain, Some(name.clone())))
} else {
None None
} }
}
fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option<ModPath> { fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option<ModPath> {
if item == from { if item == from {
@ -808,6 +827,48 @@ pub mod prelude {
); );
} }
#[test]
fn shadowed_prelude() {
check_found_path(
r#"
//- /main.rs crate:main deps:std
struct S;
$0
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub struct S;
}
}
"#,
"std::prelude::rust_2018::S",
"std::prelude::rust_2018::S",
"std::prelude::rust_2018::S",
"std::prelude::rust_2018::S",
);
}
#[test]
fn imported_prelude() {
check_found_path(
r#"
//- /main.rs crate:main deps:std
use S;
$0
//- /std.rs crate:std
pub mod prelude {
pub mod rust_2018 {
pub struct S;
}
}
"#,
"S",
"S",
"S",
"S",
);
}
#[test] #[test]
fn enum_variant_from_prelude() { fn enum_variant_from_prelude() {
let code = r#" let code = r#"