diff options
Diffstat (limited to 'crates/hir_def/src/find_path.rs')
-rw-r--r-- | crates/hir_def/src/find_path.rs | 87 |
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 | ||
3 | use std::iter; | ||
4 | |||
3 | use hir_expand::name::{known, AsName, Name}; | 5 | use hir_expand::name::{known, AsName, Name}; |
4 | use rustc_hash::FxHashSet; | 6 | use rustc_hash::FxHashSet; |
5 | use test_utils::mark; | ||
6 | 7 | ||
7 | use crate::nameres::DefMap; | 8 | use crate::nameres::DefMap; |
8 | use crate::{ | 9 | use 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( | |||
245 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { | 251 | fn 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 | } |