@@ -10,8 +10,16 @@ use crate::{
10
10
use hir_expand:: name:: Name ;
11
11
12
12
pub fn find_path ( db : & impl DefDatabase , item : ItemInNs , from : ModuleId ) -> Option < ModPath > {
13
+ find_path_inner ( db, item, from, 15 )
14
+ }
15
+
16
+ fn find_path_inner ( db : & impl DefDatabase , item : ItemInNs , from : ModuleId , max_len : usize ) -> Option < ModPath > {
13
17
// Base cases:
14
18
19
+ if max_len == 0 {
20
+ return None ;
21
+ }
22
+
15
23
// - if the item is already in scope, return the name under which it is
16
24
let def_map = db. crate_def_map ( from. krate ) ;
17
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
84
// - otherwise, look for modules containing (reexporting) it and import it from one of those
77
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
88
for ( module_id, name) in importable_locations {
80
- // TODO prevent infinite loops
81
- let mut path = match find_path ( db, ItemInNs :: Types ( ModuleDefId :: ModuleId ( module_id) ) , from)
89
+ let mut path = match find_path_inner ( db, ItemInNs :: Types ( ModuleDefId :: ModuleId ( module_id) ) , from, best_path_len - 1 )
82
90
{
83
91
None => continue ,
84
92
Some ( path) => path,
85
93
} ;
86
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
+
103
+ fn 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
92
113
fn find_importable_locations (
@@ -96,6 +117,9 @@ fn find_importable_locations(
96
117
) -> Vec < ( ModuleId , Name ) > {
97
118
let crate_graph = db. crate_graph ( ) ;
98
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
123
for krate in Some ( from. krate )
100
124
. into_iter ( )
101
125
. chain ( crate_graph. dependencies ( from. krate ) . map ( |dep| dep. crate_id ) )
@@ -110,6 +134,13 @@ fn find_importable_locations(
110
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.
113
144
pub ( crate ) fn importable_locations_in_crate_query (
114
145
db : & impl DefDatabase ,
115
146
item : ItemInNs ,
@@ -372,4 +403,22 @@ mod tests {
372
403
// crate::S would be shorter, but using private imports seems wrong
373
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
}
0 commit comments