Skip to content

Commit f17f9f5

Browse files
Merge #7834
7834: Fix `find_path` when inner items are present r=jonas-schievink a=jonas-schievink Fixes #7750 (but adds a bunch of FIXMEs, because a lot of this code is still wrong in the presence of inner items) bors r+ Co-authored-by: Jonas Schievink <[email protected]>
2 parents 9860a39 + 0dcec31 commit f17f9f5

File tree

4 files changed

+88
-28
lines changed

4 files changed

+88
-28
lines changed

crates/hir_def/src/find_path.rs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! An algorithm to find a path to refer to a certain item.
22
3+
use std::iter;
4+
35
use hir_expand::name::{known, AsName, Name};
46
use rustc_hash::FxHashSet;
57
use test_utils::mark;
@@ -95,7 +97,7 @@ fn find_path_inner(
9597
item: ItemInNs,
9698
from: ModuleId,
9799
max_len: usize,
98-
prefixed: Option<PrefixKind>,
100+
mut prefixed: Option<PrefixKind>,
99101
) -> Option<ModPath> {
100102
if max_len == 0 {
101103
return None;
@@ -114,8 +116,9 @@ fn find_path_inner(
114116
}
115117

116118
// - if the item is the crate root, return `crate`
117-
let root = def_map.module_id(def_map.root());
119+
let root = def_map.crate_root(db);
118120
if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() {
121+
// FIXME: the `block_id()` check should be unnecessary, but affects the result
119122
return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
120123
}
121124

@@ -165,7 +168,7 @@ fn find_path_inner(
165168

166169
// - otherwise, look for modules containing (reexporting) it and import it from one of those
167170

168-
let crate_root = def_map.module_id(def_map.root());
171+
let crate_root = def_map.crate_root(db);
169172
let crate_attrs = db.attrs(crate_root.into());
170173
let prefer_no_std = crate_attrs.by_key("no_std").exists();
171174
let mut best_path = None;
@@ -228,12 +231,16 @@ fn find_path_inner(
228231
}
229232
}
230233

231-
if let Some(mut prefix) = prefixed.map(PrefixKind::prefix) {
232-
if matches!(prefix, PathKind::Crate | PathKind::Super(0)) && def_map.block_id().is_some() {
233-
// Inner items cannot be referred to via `crate::` or `self::` paths.
234-
prefix = PathKind::Plain;
234+
// If the item is declared inside a block expression, don't use a prefix, as we don't handle
235+
// that correctly (FIXME).
236+
if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) {
237+
if item_module.def_map(db).block_id().is_some() && prefixed.is_some() {
238+
mark::hit!(prefixed_in_block_expression);
239+
prefixed = Some(PrefixKind::Plain);
235240
}
241+
}
236242

243+
if let Some(prefix) = prefixed.map(PrefixKind::prefix) {
237244
best_path.or_else(|| {
238245
scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
239246
})
@@ -285,12 +292,12 @@ fn find_local_import_locations(
285292
let data = &def_map[from.local_id];
286293
let mut worklist =
287294
data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>();
288-
let mut parent = data.parent;
289-
while let Some(p) = parent {
290-
worklist.push(def_map.module_id(p));
291-
parent = def_map[p].parent;
295+
for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) {
296+
worklist.push(ancestor);
292297
}
293298

299+
let def_map = def_map.crate_root(db).def_map(db);
300+
294301
let mut seen: FxHashSet<_> = FxHashSet::default();
295302

296303
let mut locations = Vec::new();
@@ -301,7 +308,14 @@ fn find_local_import_locations(
301308

302309
let ext_def_map;
303310
let data = if module.krate == from.krate {
304-
&def_map[module.local_id]
311+
if module.block.is_some() {
312+
// Re-query the block's DefMap
313+
ext_def_map = module.def_map(db);
314+
&ext_def_map[module.local_id]
315+
} else {
316+
// Reuse the root DefMap
317+
&def_map[module.local_id]
318+
}
305319
} else {
306320
// The crate might reexport a module defined in another crate.
307321
ext_def_map = module.def_map(db);
@@ -828,6 +842,7 @@ mod tests {
828842

829843
#[test]
830844
fn inner_items_from_inner_module() {
845+
mark::check!(prefixed_in_block_expression);
831846
check_found_path(
832847
r#"
833848
fn main() {
@@ -869,4 +884,24 @@ mod tests {
869884
"super::Struct",
870885
);
871886
}
887+
888+
#[test]
889+
fn outer_items_with_inner_items_present() {
890+
check_found_path(
891+
r#"
892+
mod module {
893+
pub struct CompleteMe;
894+
}
895+
896+
fn main() {
897+
fn inner() {}
898+
$0
899+
}
900+
"#,
901+
"module::CompleteMe",
902+
"module::CompleteMe",
903+
"crate::module::CompleteMe",
904+
"self::module::CompleteMe",
905+
)
906+
}
872907
}

crates/hir_def/src/item_scope.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use stdx::format_to;
1212
use test_utils::mark;
1313

1414
use crate::{
15-
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
16-
LocalModuleId, Lookup, MacroDefId, ModuleDefId, ModuleId, TraitId,
15+
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId,
16+
LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
1717
};
1818

1919
#[derive(Copy, Clone)]
@@ -375,19 +375,9 @@ impl ItemInNs {
375375

376376
/// Returns the crate defining this item (or `None` if `self` is built-in).
377377
pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
378-
Some(match self {
379-
ItemInNs::Types(did) | ItemInNs::Values(did) => match did {
380-
ModuleDefId::ModuleId(id) => id.krate,
381-
ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate,
382-
ModuleDefId::AdtId(id) => id.module(db).krate,
383-
ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate,
384-
ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate,
385-
ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate,
386-
ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate,
387-
ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate,
388-
ModuleDefId::BuiltinType(_) => return None,
389-
},
390-
ItemInNs::Macros(id) => return Some(id.krate),
391-
})
378+
match self {
379+
ItemInNs::Types(did) | ItemInNs::Values(did) => did.module(db).map(|m| m.krate),
380+
ItemInNs::Macros(id) => Some(id.krate),
381+
}
392382
}
393383
}

crates/hir_def/src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ impl ModuleId {
9797
pub fn krate(&self) -> CrateId {
9898
self.krate
9999
}
100+
101+
pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
102+
self.def_map(db).containing_module(self.local_id)
103+
}
100104
}
101105

102106
/// An ID of a module, **local** to a specific crate
@@ -529,6 +533,25 @@ impl HasModule for StaticLoc {
529533
}
530534
}
531535

536+
impl ModuleDefId {
537+
/// Returns the module containing `self` (or `self`, if `self` is itself a module).
538+
///
539+
/// Returns `None` if `self` refers to a primitive type.
540+
pub fn module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
541+
Some(match self {
542+
ModuleDefId::ModuleId(id) => *id,
543+
ModuleDefId::FunctionId(id) => id.lookup(db).module(db),
544+
ModuleDefId::AdtId(id) => id.module(db),
545+
ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db),
546+
ModuleDefId::ConstId(id) => id.lookup(db).container.module(db),
547+
ModuleDefId::StaticId(id) => id.lookup(db).container.module(db),
548+
ModuleDefId::TraitId(id) => id.lookup(db).container.module(db),
549+
ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db),
550+
ModuleDefId::BuiltinType(_) => return None,
551+
})
552+
}
553+
}
554+
532555
impl AttrDefId {
533556
pub fn krate(&self, db: &dyn db::DefDatabase) -> CrateId {
534557
match self {

crates/hir_def/src/nameres.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,18 @@ impl DefMap {
343343
Some(self.block?.parent)
344344
}
345345

346+
/// Returns the module containing `local_mod`, either the parent `mod`, or the module containing
347+
/// the block, if `self` corresponds to a block expression.
348+
pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
349+
match &self[local_mod].parent {
350+
Some(parent) => Some(self.module_id(*parent)),
351+
None => match &self.block {
352+
Some(block) => Some(block.parent),
353+
None => None,
354+
},
355+
}
356+
}
357+
346358
// FIXME: this can use some more human-readable format (ideally, an IR
347359
// even), as this should be a great debugging aid.
348360
pub fn dump(&self, db: &dyn DefDatabase) -> String {

0 commit comments

Comments
 (0)