aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/find_path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/find_path.rs')
-rw-r--r--crates/ra_hir_def/src/find_path.rs94
1 files changed, 65 insertions, 29 deletions
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index be34ee662..09f3bf87d 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -4,12 +4,11 @@ use crate::{
4 db::DefDatabase, 4 db::DefDatabase,
5 item_scope::ItemInNs, 5 item_scope::ItemInNs,
6 path::{ModPath, PathKind}, 6 path::{ModPath, PathKind},
7 ModuleId, ModuleDefId, 7 visibility::Visibility,
8 CrateId, ModuleDefId, ModuleId,
8}; 9};
9use hir_expand::name::Name; 10use hir_expand::name::Name;
10 11
11// TODO performance / memoize
12
13pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 12pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
14 // Base cases: 13 // Base cases:
15 14
@@ -21,13 +20,23 @@ pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Optio
21 } 20 }
22 21
23 // - if the item is the crate root, return `crate` 22 // - if the item is the crate root, return `crate`
24 if item == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { krate: from.krate, local_id: def_map.root })) { 23 if item
24 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
25 krate: from.krate,
26 local_id: def_map.root,
27 }))
28 {
25 return Some(ModPath::from_simple_segments(PathKind::Crate, Vec::new())); 29 return Some(ModPath::from_simple_segments(PathKind::Crate, Vec::new()));
26 } 30 }
27 31
28 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) 32 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
29 if let Some(parent_id) = def_map.modules[from.local_id].parent { 33 if let Some(parent_id) = def_map.modules[from.local_id].parent {
30 if item == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { krate: from.krate, local_id: parent_id })) { 34 if item
35 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
36 krate: from.krate,
37 local_id: parent_id,
38 }))
39 {
31 return Some(ModPath::from_simple_segments(PathKind::Super(1), Vec::new())); 40 return Some(ModPath::from_simple_segments(PathKind::Super(1), Vec::new()));
32 } 41 }
33 } 42 }
@@ -42,7 +51,8 @@ pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Optio
42 // - if the item is in the prelude, return the name from there 51 // - if the item is in the prelude, return the name from there
43 if let Some(prelude_module) = def_map.prelude { 52 if let Some(prelude_module) = def_map.prelude {
44 let prelude_def_map = db.crate_def_map(prelude_module.krate); 53 let prelude_def_map = db.crate_def_map(prelude_module.krate);
45 let prelude_scope: &crate::item_scope::ItemScope = &prelude_def_map.modules[prelude_module.local_id].scope; 54 let prelude_scope: &crate::item_scope::ItemScope =
55 &prelude_def_map.modules[prelude_module.local_id].scope;
46 if let Some((name, vis)) = prelude_scope.reverse_get(item) { 56 if let Some((name, vis)) = prelude_scope.reverse_get(item) {
47 if vis.is_visible_from(db, from) { 57 if vis.is_visible_from(db, from) {
48 return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()])); 58 return Some(ModPath::from_simple_segments(PathKind::Plain, vec![name.clone()]));
@@ -68,7 +78,8 @@ pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Optio
68 let mut candidate_paths = Vec::new(); 78 let mut candidate_paths = Vec::new();
69 for (module_id, name) in importable_locations { 79 for (module_id, name) in importable_locations {
70 // TODO prevent infinite loops 80 // TODO prevent infinite loops
71 let mut path = match find_path(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from) { 81 let mut path = match find_path(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from)
82 {
72 None => continue, 83 None => continue,
73 Some(path) => path, 84 Some(path) => path,
74 }; 85 };
@@ -78,33 +89,58 @@ pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Optio
78 candidate_paths.into_iter().min_by_key(|path| path.segments.len()) 89 candidate_paths.into_iter().min_by_key(|path| path.segments.len())
79} 90}
80 91
81fn find_importable_locations(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Vec<(ModuleId, Name)> { 92fn find_importable_locations(
93 db: &impl DefDatabase,
94 item: ItemInNs,
95 from: ModuleId,
96) -> Vec<(ModuleId, Name)> {
82 let crate_graph = db.crate_graph(); 97 let crate_graph = db.crate_graph();
83 let mut result = Vec::new(); 98 let mut result = Vec::new();
84 for krate in Some(from.krate).into_iter().chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id)) { 99 for krate in Some(from.krate)
85 let def_map = db.crate_def_map(krate); 100 .into_iter()
86 for (local_id, data) in def_map.modules.iter() { 101 .chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id))
87 if let Some((name, vis)) = data.scope.reverse_get(item) { 102 {
88 let is_private = if let crate::visibility::Visibility::Module(private_to) = vis { 103 result.extend(
89 private_to.local_id == local_id 104 db.importable_locations_in_crate(item, krate)
90 } else { false }; 105 .iter()
91 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { 106 .filter(|(_, _, vis)| vis.is_visible_from(db, from))
92 data.scope.declarations().any(|it| it == module_def_id) 107 .map(|(m, n, _)| (*m, n.clone())),
93 } else { false }; 108 );
94 if is_private && !is_original_def { 109 }
95 // Ignore private imports. these could be used if we are 110 result
96 // in a submodule of this module, but that's usually not 111}
97 // what the user wants; and if this module can import 112
98 // the item and we're a submodule of it, so can we. 113pub(crate) fn importable_locations_in_crate_query(
99 continue; 114 db: &impl DefDatabase,
100 } 115 item: ItemInNs,
101 if vis.is_visible_from(db, from) { 116 krate: CrateId,
102 result.push((ModuleId { krate, local_id }, name.clone())); 117) -> std::sync::Arc<[(ModuleId, Name, Visibility)]> {
103 } 118 let def_map = db.crate_def_map(krate);
119 let mut result = Vec::new();
120 for (local_id, data) in def_map.modules.iter() {
121 if let Some((name, vis)) = data.scope.reverse_get(item) {
122 let is_private = if let Visibility::Module(private_to) = vis {
123 private_to.local_id == local_id
124 } else {
125 false
126 };
127 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
128 data.scope.declarations().any(|it| it == module_def_id)
129 } else {
130 false
131 };
132 if is_private && !is_original_def {
133 // Ignore private imports. these could be used if we are
134 // in a submodule of this module, but that's usually not
135 // what the user wants; and if this module can import
136 // the item and we're a submodule of it, so can we.
137 // Also this keeps the cached data smaller.
138 continue;
104 } 139 }
140 result.push((ModuleId { krate, local_id }, name.clone(), vis));
105 } 141 }
106 } 142 }
107 result 143 result.into()
108} 144}
109 145
110#[cfg(test)] 146#[cfg(test)]