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.rs201
1 files changed, 153 insertions, 48 deletions
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 4a212d291..3e19a7702 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -1,10 +1,12 @@
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; 7use test_utils::mark;
6 8
7use crate::nameres::CrateDefMap; 9use crate::nameres::DefMap;
8use crate::{ 10use crate::{
9 db::DefDatabase, 11 db::DefDatabase,
10 item_scope::ItemInNs, 12 item_scope::ItemInNs,
@@ -13,8 +15,6 @@ use crate::{
13 ModuleDefId, ModuleId, 15 ModuleDefId, ModuleId,
14}; 16};
15 17
16// FIXME: handle local items
17
18/// Find a path that can be used to refer to a certain item. This can depend on 18/// Find a path that can be used to refer to a certain item. This can depend on
19/// *from where* you're referring to the item, hence the `from` parameter. 19/// *from where* you're referring to the item, hence the `from` parameter.
20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
@@ -36,29 +36,25 @@ const MAX_PATH_LEN: usize = 15;
36 36
37impl ModPath { 37impl ModPath {
38 fn starts_with_std(&self) -> bool { 38 fn starts_with_std(&self) -> bool {
39 self.segments.first() == Some(&known::std) 39 self.segments().first() == Some(&known::std)
40 } 40 }
41 41
42 // When std library is present, paths starting with `std::` 42 // When std library is present, paths starting with `std::`
43 // should be preferred over paths starting with `core::` and `alloc::` 43 // should be preferred over paths starting with `core::` and `alloc::`
44 fn can_start_with_std(&self) -> bool { 44 fn can_start_with_std(&self) -> bool {
45 let first_segment = self.segments.first(); 45 let first_segment = self.segments().first();
46 first_segment == Some(&known::alloc) || first_segment == Some(&known::core) 46 first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
47 } 47 }
48} 48}
49 49
50fn check_self_super(def_map: &CrateDefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 50fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
51 if item == ItemInNs::Types(from.into()) { 51 if item == ItemInNs::Types(from.into()) {
52 // - if the item is the module we're in, use `self` 52 // - if the item is the module we're in, use `self`
53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) 53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
54 } else if let Some(parent_id) = def_map.modules[from.local_id].parent { 54 } else if let Some(parent_id) = def_map[from.local_id].parent {
55 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) 55 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
56 if item 56 let parent_id = def_map.module_id(parent_id);
57 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { 57 if item == ItemInNs::Types(ModuleDefId::ModuleId(parent_id)) {
58 krate: from.krate,
59 local_id: parent_id,
60 }))
61 {
62 Some(ModPath::from_segments(PathKind::Super(1), Vec::new())) 58 Some(ModPath::from_segments(PathKind::Super(1), Vec::new()))
63 } else { 59 } else {
64 None 60 None
@@ -101,7 +97,7 @@ fn find_path_inner(
101 item: ItemInNs, 97 item: ItemInNs,
102 from: ModuleId, 98 from: ModuleId,
103 max_len: usize, 99 max_len: usize,
104 prefixed: Option<PrefixKind>, 100 mut prefixed: Option<PrefixKind>,
105) -> Option<ModPath> { 101) -> Option<ModPath> {
106 if max_len == 0 { 102 if max_len == 0 {
107 return None; 103 return None;
@@ -110,22 +106,19 @@ fn find_path_inner(
110 // Base cases: 106 // Base cases:
111 107
112 // - if the item is already in scope, return the name under which it is 108 // - if the item is already in scope, return the name under which it is
113 let def_map = db.crate_def_map(from.krate); 109 let def_map = from.def_map(db);
114 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; 110 let scope_name = def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
115 let scope_name = 111 def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone())
116 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None }; 112 });
117 if prefixed.is_none() && scope_name.is_some() { 113 if prefixed.is_none() && scope_name.is_some() {
118 return scope_name 114 return scope_name
119 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name])); 115 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name]));
120 } 116 }
121 117
122 // - if the item is the crate root, return `crate` 118 // - if the item is the crate root, return `crate`
123 if item 119 let root = def_map.crate_root(db);
124 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { 120 if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() {
125 krate: from.krate, 121 // FIXME: the `block_id()` check should be unnecessary, but affects the result
126 local_id: def_map.root,
127 }))
128 {
129 return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); 122 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
130 } 123 }
131 124
@@ -136,7 +129,7 @@ fn find_path_inner(
136 } 129 }
137 130
138 // - if the item is the crate root of a dependency crate, return the name from the extern prelude 131 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
139 for (name, def_id) in &def_map.extern_prelude { 132 for (name, def_id) in def_map.extern_prelude() {
140 if item == ItemInNs::Types(*def_id) { 133 if item == ItemInNs::Types(*def_id) {
141 let name = scope_name.unwrap_or_else(|| name.clone()); 134 let name = scope_name.unwrap_or_else(|| name.clone());
142 return Some(ModPath::from_segments(PathKind::Plain, vec![name])); 135 return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
@@ -144,10 +137,10 @@ fn find_path_inner(
144 } 137 }
145 138
146 // - if the item is in the prelude, return the name from there 139 // - if the item is in the prelude, return the name from there
147 if let Some(prelude_module) = def_map.prelude { 140 if let Some(prelude_module) = def_map.prelude() {
148 let prelude_def_map = db.crate_def_map(prelude_module.krate); 141 let prelude_def_map = prelude_module.def_map(db);
149 let prelude_scope: &crate::item_scope::ItemScope = 142 let prelude_scope: &crate::item_scope::ItemScope =
150 &prelude_def_map.modules[prelude_module.local_id].scope; 143 &prelude_def_map[prelude_module.local_id].scope;
151 if let Some((name, vis)) = prelude_scope.name_of(item) { 144 if let Some((name, vis)) = prelude_scope.name_of(item) {
152 if vis.is_visible_from(db, from) { 145 if vis.is_visible_from(db, from) {
153 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()])); 146 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
@@ -165,7 +158,7 @@ fn find_path_inner(
165 if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { 158 if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
166 if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) { 159 if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) {
167 let data = db.enum_data(variant.parent); 160 let data = db.enum_data(variant.parent);
168 path.segments.push(data.variants[variant.local_id].name.clone()); 161 path.push_segment(data.variants[variant.local_id].name.clone());
169 return Some(path); 162 return Some(path);
170 } 163 }
171 // If this doesn't work, it seems we have no way of referring to the 164 // If this doesn't work, it seems we have no way of referring to the
@@ -175,7 +168,7 @@ fn find_path_inner(
175 168
176 // - otherwise, look for modules containing (reexporting) it and import it from one of those 169 // - otherwise, look for modules containing (reexporting) it and import it from one of those
177 170
178 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; 171 let crate_root = def_map.crate_root(db);
179 let crate_attrs = db.attrs(crate_root.into()); 172 let crate_attrs = db.attrs(crate_root.into());
180 let prefer_no_std = crate_attrs.by_key("no_std").exists(); 173 let prefer_no_std = crate_attrs.by_key("no_std").exists();
181 let mut best_path = None; 174 let mut best_path = None;
@@ -194,7 +187,7 @@ fn find_path_inner(
194 best_path_len - 1, 187 best_path_len - 1,
195 prefixed, 188 prefixed,
196 ) { 189 ) {
197 path.segments.push(name); 190 path.push_segment(name);
198 191
199 let new_path = if let Some(best_path) = best_path { 192 let new_path = if let Some(best_path) = best_path {
200 select_best_path(best_path, path, prefer_no_std) 193 select_best_path(best_path, path, prefer_no_std)
@@ -223,7 +216,7 @@ fn find_path_inner(
223 prefixed, 216 prefixed,
224 )?; 217 )?;
225 mark::hit!(partially_imported); 218 mark::hit!(partially_imported);
226 path.segments.push(info.path.segments.last().unwrap().clone()); 219 path.push_segment(info.path.segments.last().unwrap().clone());
227 Some(path) 220 Some(path)
228 }) 221 })
229 }); 222 });
@@ -238,6 +231,15 @@ fn find_path_inner(
238 } 231 }
239 } 232 }
240 233
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);
240 }
241 }
242
241 if let Some(prefix) = prefixed.map(PrefixKind::prefix) { 243 if let Some(prefix) = prefixed.map(PrefixKind::prefix) {
242 best_path.or_else(|| { 244 best_path.or_else(|| {
243 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name])) 245 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
@@ -283,22 +285,19 @@ fn find_local_import_locations(
283 // above `from` with any visibility. That means we do not need to descend into private siblings 285 // above `from` with any visibility. That means we do not need to descend into private siblings
284 // of `from` (and similar). 286 // of `from` (and similar).
285 287
286 let def_map = db.crate_def_map(from.krate); 288 let def_map = from.def_map(db);
287 289
288 // Compute the initial worklist. We start with all direct child modules of `from` as well as all 290 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
289 // of its (recursive) parent modules. 291 // of its (recursive) parent modules.
290 let data = &def_map.modules[from.local_id]; 292 let data = &def_map[from.local_id];
291 let mut worklist = data 293 let mut worklist =
292 .children 294 data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>();
293 .values() 295 for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) {
294 .map(|child| ModuleId { krate: from.krate, local_id: *child }) 296 worklist.push(ancestor);
295 .collect::<Vec<_>>();
296 let mut parent = data.parent;
297 while let Some(p) = parent {
298 worklist.push(ModuleId { krate: from.krate, local_id: p });
299 parent = def_map.modules[p].parent;
300 } 297 }
301 298
299 let def_map = def_map.crate_root(db).def_map(db);
300
302 let mut seen: FxHashSet<_> = FxHashSet::default(); 301 let mut seen: FxHashSet<_> = FxHashSet::default();
303 302
304 let mut locations = Vec::new(); 303 let mut locations = Vec::new();
@@ -309,10 +308,17 @@ fn find_local_import_locations(
309 308
310 let ext_def_map; 309 let ext_def_map;
311 let data = if module.krate == from.krate { 310 let data = if module.krate == from.krate {
312 &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 }
313 } else { 319 } else {
314 // The crate might reexport a module defined in another crate. 320 // The crate might reexport a module defined in another crate.
315 ext_def_map = db.crate_def_map(module.krate); 321 ext_def_map = module.def_map(db);
316 &ext_def_map[module.local_id] 322 &ext_def_map[module.local_id]
317 }; 323 };
318 324
@@ -369,14 +375,14 @@ mod tests {
369 /// module the cursor is in. 375 /// module the cursor is in.
370 fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) { 376 fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) {
371 let (db, pos) = TestDB::with_position(ra_fixture); 377 let (db, pos) = TestDB::with_position(ra_fixture);
372 let module = db.module_for_file(pos.file_id); 378 let module = db.module_at_position(pos);
373 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); 379 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
374 let ast_path = 380 let ast_path =
375 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); 381 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
376 let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap(); 382 let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
377 383
378 let crate_def_map = db.crate_def_map(module.krate); 384 let def_map = module.def_map(&db);
379 let resolved = crate_def_map 385 let resolved = def_map
380 .resolve_path( 386 .resolve_path(
381 &db, 387 &db,
382 module.local_id, 388 module.local_id,
@@ -799,4 +805,103 @@ mod tests {
799 check_found_path(code, "u8", "u8", "u8", "u8"); 805 check_found_path(code, "u8", "u8", "u8", "u8");
800 check_found_path(code, "u16", "u16", "u16", "u16"); 806 check_found_path(code, "u16", "u16", "u16", "u16");
801 } 807 }
808
809 #[test]
810 fn inner_items() {
811 check_found_path(
812 r#"
813 fn main() {
814 struct Inner {}
815 $0
816 }
817 "#,
818 "Inner",
819 "Inner",
820 "Inner",
821 "Inner",
822 );
823 }
824
825 #[test]
826 fn inner_items_from_outer_scope() {
827 check_found_path(
828 r#"
829 fn main() {
830 struct Struct {}
831 {
832 $0
833 }
834 }
835 "#,
836 "Struct",
837 "Struct",
838 "Struct",
839 "Struct",
840 );
841 }
842
843 #[test]
844 fn inner_items_from_inner_module() {
845 mark::check!(prefixed_in_block_expression);
846 check_found_path(
847 r#"
848 fn main() {
849 mod module {
850 struct Struct {}
851 }
852 {
853 $0
854 }
855 }
856 "#,
857 "module::Struct",
858 "module::Struct",
859 "module::Struct",
860 "module::Struct",
861 );
862 }
863
864 #[test]
865 #[ignore]
866 fn inner_items_from_parent_module() {
867 // FIXME: ItemTree currently associates all inner items with `main`. Luckily, this sort of
868 // code is very rare, so this isn't terrible.
869 // To fix it, we should probably build dedicated `ItemTree`s for inner items, and not store
870 // them in the file's main ItemTree. This would also allow us to stop parsing function
871 // bodies when we only want to compute the crate's main DefMap.
872 check_found_path(
873 r#"
874 fn main() {
875 struct Struct {}
876 mod module {
877 $0
878 }
879 }
880 "#,
881 "super::Struct",
882 "super::Struct",
883 "super::Struct",
884 "super::Struct",
885 );
886 }
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 }
802} 907}