aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/find_path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/find_path.rs')
-rw-r--r--crates/hir_def/src/find_path.rs87
1 files changed, 48 insertions, 39 deletions
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 5e2a711b8..de08e2737 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -1,8 +1,9 @@
1//! An algorithm to find a path to refer to a certain item. 1//! An algorithm to find a path to refer to a certain item.
2 2
3use std::iter;
4
3use hir_expand::name::{known, AsName, Name}; 5use hir_expand::name::{known, AsName, Name};
4use rustc_hash::FxHashSet; 6use rustc_hash::FxHashSet;
5use test_utils::mark;
6 7
7use crate::nameres::DefMap; 8use crate::nameres::DefMap;
8use crate::{ 9use crate::{
@@ -95,7 +96,7 @@ fn find_path_inner(
95 item: ItemInNs, 96 item: ItemInNs,
96 from: ModuleId, 97 from: ModuleId,
97 max_len: usize, 98 max_len: usize,
98 prefixed: Option<PrefixKind>, 99 mut prefixed: Option<PrefixKind>,
99) -> Option<ModPath> { 100) -> Option<ModPath> {
100 if max_len == 0 { 101 if max_len == 0 {
101 return None; 102 return None;
@@ -114,8 +115,9 @@ fn find_path_inner(
114 } 115 }
115 116
116 // - if the item is the crate root, return `crate` 117 // - if the item is the crate root, return `crate`
117 let root = def_map.module_id(def_map.root()); 118 let root = def_map.crate_root(db);
118 if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() { 119 if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() {
120 // FIXME: the `block_id()` check should be unnecessary, but affects the result
119 return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); 121 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
120 } 122 }
121 123
@@ -165,7 +167,7 @@ fn find_path_inner(
165 167
166 // - otherwise, look for modules containing (reexporting) it and import it from one of those 168 // - otherwise, look for modules containing (reexporting) it and import it from one of those
167 169
168 let crate_root = def_map.module_id(def_map.root()); 170 let crate_root = def_map.crate_root(db);
169 let crate_attrs = db.attrs(crate_root.into()); 171 let crate_attrs = db.attrs(crate_root.into());
170 let prefer_no_std = crate_attrs.by_key("no_std").exists(); 172 let prefer_no_std = crate_attrs.by_key("no_std").exists();
171 let mut best_path = None; 173 let mut best_path = None;
@@ -212,7 +214,7 @@ fn find_path_inner(
212 best_path_len - 1, 214 best_path_len - 1,
213 prefixed, 215 prefixed,
214 )?; 216 )?;
215 mark::hit!(partially_imported); 217 cov_mark::hit!(partially_imported);
216 path.push_segment(info.path.segments.last().unwrap().clone()); 218 path.push_segment(info.path.segments.last().unwrap().clone());
217 Some(path) 219 Some(path)
218 }) 220 })
@@ -228,12 +230,16 @@ fn find_path_inner(
228 } 230 }
229 } 231 }
230 232
231 if let Some(mut prefix) = prefixed.map(PrefixKind::prefix) { 233 // If the item is declared inside a block expression, don't use a prefix, as we don't handle
232 if matches!(prefix, PathKind::Crate | PathKind::Super(0)) && def_map.block_id().is_some() { 234 // that correctly (FIXME).
233 // Inner items cannot be referred to via `crate::` or `self::` paths. 235 if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) {
234 prefix = PathKind::Plain; 236 if item_module.def_map(db).block_id().is_some() && prefixed.is_some() {
237 cov_mark::hit!(prefixed_in_block_expression);
238 prefixed = Some(PrefixKind::Plain);
235 } 239 }
240 }
236 241
242 if let Some(prefix) = prefixed.map(PrefixKind::prefix) {
237 best_path.or_else(|| { 243 best_path.or_else(|| {
238 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name])) 244 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
239 }) 245 })
@@ -245,18 +251,18 @@ fn find_path_inner(
245fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { 251fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
246 if old_path.starts_with_std() && new_path.can_start_with_std() { 252 if old_path.starts_with_std() && new_path.can_start_with_std() {
247 if prefer_no_std { 253 if prefer_no_std {
248 mark::hit!(prefer_no_std_paths); 254 cov_mark::hit!(prefer_no_std_paths);
249 new_path 255 new_path
250 } else { 256 } else {
251 mark::hit!(prefer_std_paths); 257 cov_mark::hit!(prefer_std_paths);
252 old_path 258 old_path
253 } 259 }
254 } else if new_path.starts_with_std() && old_path.can_start_with_std() { 260 } else if new_path.starts_with_std() && old_path.can_start_with_std() {
255 if prefer_no_std { 261 if prefer_no_std {
256 mark::hit!(prefer_no_std_paths); 262 cov_mark::hit!(prefer_no_std_paths);
257 old_path 263 old_path
258 } else { 264 } else {
259 mark::hit!(prefer_std_paths); 265 cov_mark::hit!(prefer_std_paths);
260 new_path 266 new_path
261 } 267 }
262 } else if new_path.len() < old_path.len() { 268 } else if new_path.len() < old_path.len() {
@@ -285,12 +291,12 @@ fn find_local_import_locations(
285 let data = &def_map[from.local_id]; 291 let data = &def_map[from.local_id];
286 let mut worklist = 292 let mut worklist =
287 data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>(); 293 data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>();
288 let mut parent = data.parent; 294 for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) {
289 while let Some(p) = parent { 295 worklist.push(ancestor);
290 worklist.push(def_map.module_id(p));
291 parent = def_map[p].parent;
292 } 296 }
293 297
298 let def_map = def_map.crate_root(db).def_map(db);
299
294 let mut seen: FxHashSet<_> = FxHashSet::default(); 300 let mut seen: FxHashSet<_> = FxHashSet::default();
295 301
296 let mut locations = Vec::new(); 302 let mut locations = Vec::new();
@@ -301,7 +307,14 @@ fn find_local_import_locations(
301 307
302 let ext_def_map; 308 let ext_def_map;
303 let data = if module.krate == from.krate { 309 let data = if module.krate == from.krate {
304 &def_map[module.local_id] 310 if module.block.is_some() {
311 // Re-query the block's DefMap
312 ext_def_map = module.def_map(db);
313 &ext_def_map[module.local_id]
314 } else {
315 // Reuse the root DefMap
316 &def_map[module.local_id]
317 }
305 } else { 318 } else {
306 // The crate might reexport a module defined in another crate. 319 // The crate might reexport a module defined in another crate.
307 ext_def_map = module.def_map(db); 320 ext_def_map = module.def_map(db);
@@ -350,7 +363,6 @@ mod tests {
350 use base_db::fixture::WithFixture; 363 use base_db::fixture::WithFixture;
351 use hir_expand::hygiene::Hygiene; 364 use hir_expand::hygiene::Hygiene;
352 use syntax::ast::AstNode; 365 use syntax::ast::AstNode;
353 use test_utils::mark;
354 366
355 use crate::test_db::TestDB; 367 use crate::test_db::TestDB;
356 368
@@ -508,7 +520,7 @@ mod tests {
508 520
509 #[test] 521 #[test]
510 fn partially_imported() { 522 fn partially_imported() {
511 mark::check!(partially_imported); 523 cov_mark::check!(partially_imported);
512 // Tests that short paths are used even for external items, when parts of the path are 524 // Tests that short paths are used even for external items, when parts of the path are
513 // already in scope. 525 // already in scope.
514 let code = r#" 526 let code = r#"
@@ -672,7 +684,7 @@ mod tests {
672 684
673 #[test] 685 #[test]
674 fn prefer_std_paths_over_alloc() { 686 fn prefer_std_paths_over_alloc() {
675 mark::check!(prefer_std_paths); 687 cov_mark::check!(prefer_std_paths);
676 let code = r#" 688 let code = r#"
677 //- /main.rs crate:main deps:alloc,std 689 //- /main.rs crate:main deps:alloc,std
678 $0 690 $0
@@ -698,7 +710,7 @@ mod tests {
698 710
699 #[test] 711 #[test]
700 fn prefer_core_paths_over_std() { 712 fn prefer_core_paths_over_std() {
701 mark::check!(prefer_no_std_paths); 713 cov_mark::check!(prefer_no_std_paths);
702 let code = r#" 714 let code = r#"
703 //- /main.rs crate:main deps:core,std 715 //- /main.rs crate:main deps:core,std
704 #![no_std] 716 #![no_std]
@@ -828,6 +840,7 @@ mod tests {
828 840
829 #[test] 841 #[test]
830 fn inner_items_from_inner_module() { 842 fn inner_items_from_inner_module() {
843 cov_mark::check!(prefixed_in_block_expression);
831 check_found_path( 844 check_found_path(
832 r#" 845 r#"
833 fn main() { 846 fn main() {
@@ -847,26 +860,22 @@ mod tests {
847 } 860 }
848 861
849 #[test] 862 #[test]
850 #[ignore] 863 fn outer_items_with_inner_items_present() {
851 fn inner_items_from_parent_module() {
852 // FIXME: ItemTree currently associates all inner items with `main`. Luckily, this sort of
853 // code is very rare, so this isn't terrible.
854 // To fix it, we should probably build dedicated `ItemTree`s for inner items, and not store
855 // them in the file's main ItemTree. This would also allow us to stop parsing function
856 // bodies when we only want to compute the crate's main DefMap.
857 check_found_path( 864 check_found_path(
858 r#" 865 r#"
866 mod module {
867 pub struct CompleteMe;
868 }
869
859 fn main() { 870 fn main() {
860 struct Struct {} 871 fn inner() {}
861 mod module { 872 $0
862 $0
863 }
864 } 873 }
865 "#, 874 "#,
866 "super::Struct", 875 "module::CompleteMe",
867 "super::Struct", 876 "module::CompleteMe",
868 "super::Struct", 877 "crate::module::CompleteMe",
869 "super::Struct", 878 "self::module::CompleteMe",
870 ); 879 )
871 } 880 }
872} 881}