@@ -4,23 +4,82 @@ use crate::{
4
4
db:: DefDatabase ,
5
5
item_scope:: ItemInNs ,
6
6
path:: { ModPath , PathKind } ,
7
- ModuleId ,
7
+ ModuleId , ModuleDefId ,
8
8
} ;
9
+ use hir_expand:: name:: Name ;
9
10
10
- pub fn find_path ( db : & impl DefDatabase , item : ItemInNs , from : ModuleId ) -> ModPath {
11
+ // TODO handle prelude
12
+ // TODO handle enum variants
13
+ // TODO don't import from super imports? or at least deprioritize
14
+ // TODO use super?
15
+ // TODO use shortest path
16
+ // TODO performance / memoize
17
+
18
+ pub fn find_path ( db : & impl DefDatabase , item : ItemInNs , from : ModuleId ) -> Option < ModPath > {
11
19
// 1. Find all locations that the item could be imported from (i.e. that are visible)
12
20
// - this needs to consider other crates, for reexports from transitive dependencies
13
21
// - filter by visibility
14
22
// 2. For each of these, go up the module tree until we find an
15
23
// item/module/crate that is already in scope (including because it is in
16
24
// the prelude, and including aliases!)
17
25
// 3. Then select the one that gives the shortest path
26
+
27
+ // Base cases:
28
+
29
+ // - if the item is already in scope, return the name under which it is
18
30
let def_map = db. crate_def_map ( from. krate ) ;
19
31
let from_scope: & crate :: item_scope:: ItemScope = & def_map. modules [ from. local_id ] . scope ;
20
32
if let Some ( ( name, _) ) = from_scope. reverse_get ( item) {
21
- return ModPath :: from_simple_segments ( PathKind :: Plain , vec ! [ name. clone( ) ] ) ;
33
+ return Some ( ModPath :: from_simple_segments ( PathKind :: Plain , vec ! [ name. clone( ) ] ) ) ;
34
+ }
35
+
36
+ // - if the item is the crate root, return `crate`
37
+ if item == ItemInNs :: Types ( ModuleDefId :: ModuleId ( ModuleId { krate : from. krate , local_id : def_map. root } ) ) {
38
+ return Some ( ModPath :: from_simple_segments ( PathKind :: Crate , Vec :: new ( ) ) ) ;
39
+ }
40
+
41
+ // - if the item is the crate root of a dependency crate, return the name from the extern prelude
42
+ for ( name, def_id) in & def_map. extern_prelude {
43
+ if item == ItemInNs :: Types ( * def_id) {
44
+ return Some ( ModPath :: from_simple_segments ( PathKind :: Plain , vec ! [ name. clone( ) ] ) ) ;
45
+ }
46
+ }
47
+
48
+ // - if the item is in the prelude, return the name from there
49
+ // TODO check prelude
50
+
51
+ // Recursive case:
52
+ // - if the item is an enum variant, refer to it via the enum
53
+
54
+ // - otherwise, look for modules containing (reexporting) it and import it from one of those
55
+ let importable_locations = find_importable_locations ( db, item, from) ;
56
+ // XXX going in order for now
57
+ for ( module_id, name) in importable_locations {
58
+ // TODO prevent infinite loops
59
+ let mut path = match find_path ( db, ItemInNs :: Types ( ModuleDefId :: ModuleId ( module_id) ) , from) {
60
+ None => continue ,
61
+ Some ( path) => path,
62
+ } ;
63
+ path. segments . push ( name) ;
64
+ return Some ( path) ;
65
+ }
66
+ None
67
+ }
68
+
69
+ fn find_importable_locations ( db : & impl DefDatabase , item : ItemInNs , from : ModuleId ) -> Vec < ( ModuleId , Name ) > {
70
+ let crate_graph = db. crate_graph ( ) ;
71
+ let mut result = Vec :: new ( ) ;
72
+ for krate in Some ( from. krate ) . into_iter ( ) . chain ( crate_graph. dependencies ( from. krate ) . map ( |dep| dep. crate_id ) ) {
73
+ let def_map = db. crate_def_map ( krate) ;
74
+ for ( local_id, data) in def_map. modules . iter ( ) {
75
+ if let Some ( ( name, vis) ) = data. scope . reverse_get ( item) {
76
+ if vis. is_visible_from ( db, from) {
77
+ result. push ( ( ModuleId { krate, local_id } , name. clone ( ) ) ) ;
78
+ }
79
+ }
80
+ }
22
81
}
23
- todo ! ( )
82
+ result
24
83
}
25
84
26
85
#[ cfg( test) ]
@@ -59,7 +118,7 @@ mod tests {
59
118
60
119
let found_path = find_path ( & db, ItemInNs :: Types ( resolved) , module) ;
61
120
62
- assert_eq ! ( mod_path , found_path ) ;
121
+ assert_eq ! ( found_path , Some ( mod_path ) ) ;
63
122
}
64
123
65
124
#[ test]
@@ -84,6 +143,17 @@ mod tests {
84
143
check_found_path ( code, "foo::S" ) ;
85
144
}
86
145
146
+ #[ test]
147
+ fn crate_root ( ) {
148
+ let code = r#"
149
+ //- /main.rs
150
+ mod foo;
151
+ //- /foo.rs
152
+ <|>
153
+ "# ;
154
+ check_found_path ( code, "crate" ) ;
155
+ }
156
+
87
157
#[ test]
88
158
fn same_crate ( ) {
89
159
let code = r#"
@@ -107,6 +177,18 @@ mod tests {
107
177
check_found_path ( code, "std::S" ) ;
108
178
}
109
179
180
+ #[ test]
181
+ fn different_crate_renamed ( ) {
182
+ let code = r#"
183
+ //- /main.rs crate:main deps:std
184
+ extern crate std as std_renamed;
185
+ <|>
186
+ //- /std.rs crate:std
187
+ pub struct S;
188
+ "# ;
189
+ check_found_path ( code, "std_renamed::S" ) ;
190
+ }
191
+
110
192
#[ test]
111
193
fn same_crate_reexport ( ) {
112
194
let code = r#"
@@ -119,4 +201,30 @@ mod tests {
119
201
"# ;
120
202
check_found_path ( code, "bar::S" ) ;
121
203
}
204
+
205
+ #[ test]
206
+ fn same_crate_reexport_rename ( ) {
207
+ let code = r#"
208
+ //- /main.rs
209
+ mod bar {
210
+ mod foo { pub(super) struct S; }
211
+ pub(crate) use foo::S as U;
212
+ }
213
+ <|>
214
+ "# ;
215
+ check_found_path ( code, "bar::U" ) ;
216
+ }
217
+
218
+ #[ test]
219
+ fn prelude ( ) {
220
+ let code = r#"
221
+ //- /main.rs crate:main deps:std
222
+ <|>
223
+ //- /std.rs crate:std
224
+ pub mod prelude { pub struct S; }
225
+ #[prelude_import]
226
+ pub use prelude::*;
227
+ "# ;
228
+ check_found_path ( code, "S" ) ;
229
+ }
122
230
}
0 commit comments