diff options
Diffstat (limited to 'crates/hir_def/src/find_path.rs')
-rw-r--r-- | crates/hir_def/src/find_path.rs | 201 |
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 | ||
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; | 7 | use test_utils::mark; |
6 | 8 | ||
7 | use crate::nameres::CrateDefMap; | 9 | use crate::nameres::DefMap; |
8 | use crate::{ | 10 | use 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. |
20 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 20 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { |
@@ -36,29 +36,25 @@ const MAX_PATH_LEN: usize = 15; | |||
36 | 36 | ||
37 | impl ModPath { | 37 | impl 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 | ||
50 | fn check_self_super(def_map: &CrateDefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 50 | fn 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 | } |