aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r--crates/ra_hir_def/src/find_path.rs59
1 files changed, 54 insertions, 5 deletions
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 09f3bf87d..3df2f2c09 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -10,8 +10,16 @@ use crate::{
10use hir_expand::name::Name; 10use hir_expand::name::Name;
11 11
12pub 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> {
13 find_path_inner(db, item, from, 15)
14}
15
16fn find_path_inner(db: &impl DefDatabase, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath> {
13 // Base cases: 17 // Base cases:
14 18
19 if max_len == 0 {
20 return None;
21 }
22
15 // - if the item is already in scope, return the name under which it is 23 // - if the item is already in scope, return the name under which it is
16 let def_map = db.crate_def_map(from.krate); 24 let def_map = db.crate_def_map(from.krate);
17 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; 25 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
@@ -75,18 +83,31 @@ pub fn find_path(db: &impl DefDatabase, item: ItemInNs, from: ModuleId) -> Optio
75 83
76 // - otherwise, look for modules containing (reexporting) it and import it from one of those 84 // - otherwise, look for modules containing (reexporting) it and import it from one of those
77 let importable_locations = find_importable_locations(db, item, from); 85 let importable_locations = find_importable_locations(db, item, from);
78 let mut candidate_paths = Vec::new(); 86 let mut best_path = None;
87 let mut best_path_len = max_len;
79 for (module_id, name) in importable_locations { 88 for (module_id, name) in importable_locations {
80 // TODO prevent infinite loops 89 let mut path = match find_path_inner(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from, best_path_len - 1)
81 let mut path = match find_path(db, ItemInNs::Types(ModuleDefId::ModuleId(module_id)), from)
82 { 90 {
83 None => continue, 91 None => continue,
84 Some(path) => path, 92 Some(path) => path,
85 }; 93 };
86 path.segments.push(name); 94 path.segments.push(name);
87 candidate_paths.push(path); 95 if path_len(&path) < best_path_len {
96 best_path_len = path_len(&path);
97 best_path = Some(path);
98 }
99 }
100 best_path
101}
102
103fn path_len(path: &ModPath) -> usize {
104 path.segments.len() + match path.kind {
105 PathKind::Plain => 0,
106 PathKind::Super(i) => i as usize,
107 PathKind::Crate => 1,
108 PathKind::Abs => 0,
109 PathKind::DollarCrate(_) => 1,
88 } 110 }
89 candidate_paths.into_iter().min_by_key(|path| path.segments.len())
90} 111}
91 112
92fn find_importable_locations( 113fn find_importable_locations(
@@ -96,6 +117,9 @@ fn find_importable_locations(
96) -> Vec<(ModuleId, Name)> { 117) -> Vec<(ModuleId, Name)> {
97 let crate_graph = db.crate_graph(); 118 let crate_graph = db.crate_graph();
98 let mut result = Vec::new(); 119 let mut result = Vec::new();
120 // We only look in the crate from which we are importing, and the direct
121 // dependencies. We cannot refer to names from transitive dependencies
122 // directly (only through reexports in direct dependencies).
99 for krate in Some(from.krate) 123 for krate in Some(from.krate)
100 .into_iter() 124 .into_iter()
101 .chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id)) 125 .chain(crate_graph.dependencies(from.krate).map(|dep| dep.crate_id))
@@ -110,6 +134,13 @@ fn find_importable_locations(
110 result 134 result
111} 135}
112 136
137/// Collects all locations from which we might import the item in a particular
138/// crate. These include the original definition of the item, and any
139/// non-private `use`s.
140///
141/// Note that the crate doesn't need to be the one in which the item is defined;
142/// it might be re-exported in other crates. We cache this as a query since we
143/// need to walk the whole def map for it.
113pub(crate) fn importable_locations_in_crate_query( 144pub(crate) fn importable_locations_in_crate_query(
114 db: &impl DefDatabase, 145 db: &impl DefDatabase,
115 item: ItemInNs, 146 item: ItemInNs,
@@ -372,4 +403,22 @@ mod tests {
372 // crate::S would be shorter, but using private imports seems wrong 403 // crate::S would be shorter, but using private imports seems wrong
373 check_found_path(code, "crate::bar::S"); 404 check_found_path(code, "crate::bar::S");
374 } 405 }
406
407 #[test]
408 fn import_cycle() {
409 let code = r#"
410 //- /main.rs
411 pub mod foo;
412 pub mod bar;
413 pub mod baz;
414 //- /bar.rs
415 <|>
416 //- /foo.rs
417 pub use super::baz;
418 pub struct S;
419 //- /baz.rs
420 pub use super::foo;
421 "#;
422 check_found_path(code, "crate::foo::S");
423 }
375} 424}