diff options
| author | Ryo Yoshida <low.ryoshida@gmail.com> | 2023-01-02 20:31:35 +0900 |
|---|---|---|
| committer | Ryo Yoshida <low.ryoshida@gmail.com> | 2023-01-02 20:50:45 +0900 |
| commit | cf2fa14db515e1b14281f436f8b793e23d83b387 (patch) | |
| tree | 48687f4439a289e0a5881cd64b311f2460ed9b24 | |
| parent | 643bc02ded7141a462bc0c9357c8914f9d50fb06 (diff) | |
| download | rust-cf2fa14db515e1b14281f436f8b793e23d83b387.tar.gz rust-cf2fa14db515e1b14281f436f8b793e23d83b387.zip | |
fix: prefix prelude items whose name collides in current scope
| -rw-r--r-- | crates/hir-def/src/find_path.rs | 87 |
1 files changed, 74 insertions, 13 deletions
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index e90dc47cf38..f7dc3a73654 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -107,7 +107,7 @@ fn find_path_inner( } // - 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; } @@ -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; } @@ -234,23 +235,41 @@ 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( db: &dyn DefDatabase, root_def_map: &DefMap, + local_def_map: &DefMap, item: ItemInNs, from: ModuleId, -) -> Option<Option<ModPath>> { - if let Some(prelude_module) = root_def_map.prelude() { - // Preludes in block DefMaps are ignored, only the crate DefMap is searched - let prelude_def_map = prelude_module.def_map(db); - let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; - if let Some((name, vis)) = prelude_scope.name_of(item) { - if vis.is_visible_from(db, from) { - return Some(Some(ModPath::from_segments(PathKind::Plain, Some(name.clone())))); - } - } +) -> Option<ModPath> { + let prelude_module = root_def_map.prelude()?; + // Preludes in block DefMaps are ignored, only the crate DefMap is searched + let prelude_def_map = prelude_module.def_map(db); + let prelude_scope = &prelude_def_map[prelude_module.local_id].scope; + let (name, vis) = prelude_scope.name_of(item)?; + if !vis.is_visible_from(db, from) { + 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> { @@ -809,6 +828,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] fn enum_variant_from_prelude() { let code = r#" //- /main.rs crate:main deps:std |
