1
1
//! An algorithm to find a path to refer to a certain item.
2
2
3
- use std:: sync:: Arc ;
4
-
5
3
use hir_expand:: name:: { known, AsName , Name } ;
6
4
use ra_prof:: profile;
5
+ use rustc_hash:: FxHashSet ;
7
6
use test_utils:: mark;
8
7
9
8
use crate :: {
10
9
db:: DefDatabase ,
11
10
item_scope:: ItemInNs ,
12
11
path:: { ModPath , PathKind } ,
13
12
visibility:: Visibility ,
14
- CrateId , ModuleDefId , ModuleId ,
13
+ ModuleDefId , ModuleId ,
15
14
} ;
16
15
17
16
// FIXME: handle local items
@@ -20,7 +19,7 @@ use crate::{
20
19
/// *from where* you're referring to the item, hence the `from` parameter.
21
20
pub fn find_path ( db : & dyn DefDatabase , item : ItemInNs , from : ModuleId ) -> Option < ModPath > {
22
21
let _p = profile ( "find_path" ) ;
23
- db . find_path_inner ( item, from, MAX_PATH_LEN )
22
+ find_path_inner ( db , item, from, MAX_PATH_LEN )
24
23
}
25
24
26
25
const MAX_PATH_LEN : usize = 15 ;
@@ -36,20 +35,9 @@ impl ModPath {
36
35
let first_segment = self . segments . first ( ) ;
37
36
first_segment == Some ( & known:: alloc) || first_segment == Some ( & known:: core)
38
37
}
39
-
40
- fn len ( & self ) -> usize {
41
- self . segments . len ( )
42
- + match self . kind {
43
- PathKind :: Plain => 0 ,
44
- PathKind :: Super ( i) => i as usize ,
45
- PathKind :: Crate => 1 ,
46
- PathKind :: Abs => 0 ,
47
- PathKind :: DollarCrate ( _) => 1 ,
48
- }
49
- }
50
38
}
51
39
52
- pub ( crate ) fn find_path_inner_query (
40
+ fn find_path_inner (
53
41
db : & dyn DefDatabase ,
54
42
item : ItemInNs ,
55
43
from : ModuleId ,
@@ -133,31 +121,61 @@ pub(crate) fn find_path_inner_query(
133
121
}
134
122
135
123
// - otherwise, look for modules containing (reexporting) it and import it from one of those
124
+
136
125
let crate_root = ModuleId { local_id : def_map. root , krate : from. krate } ;
137
126
let crate_attrs = db. attrs ( crate_root. into ( ) ) ;
138
127
let prefer_no_std = crate_attrs. by_key ( "no_std" ) . exists ( ) ;
139
- let importable_locations = find_importable_locations ( db, item, from) ;
140
128
let mut best_path = None ;
141
129
let mut best_path_len = max_len;
142
- for ( module_id, name) in importable_locations {
143
- let mut path = match db. find_path_inner (
144
- ItemInNs :: Types ( ModuleDefId :: ModuleId ( module_id) ) ,
145
- from,
146
- best_path_len - 1 ,
147
- ) {
148
- None => continue ,
149
- Some ( path) => path,
150
- } ;
151
- path. segments . push ( name) ;
152
130
153
- let new_path = if let Some ( best_path) = best_path {
154
- select_best_path ( best_path, path, prefer_no_std)
155
- } else {
156
- path
157
- } ;
158
- best_path_len = new_path. len ( ) ;
159
- best_path = Some ( new_path) ;
131
+ if item. krate ( db) == Some ( from. krate ) {
132
+ // Item was defined in the same crate that wants to import it. It cannot be found in any
133
+ // dependency in this case.
134
+
135
+ let local_imports = find_local_import_locations ( db, item, from) ;
136
+ for ( module_id, name) in local_imports {
137
+ if let Some ( mut path) = find_path_inner (
138
+ db,
139
+ ItemInNs :: Types ( ModuleDefId :: ModuleId ( module_id) ) ,
140
+ from,
141
+ best_path_len - 1 ,
142
+ ) {
143
+ path. segments . push ( name) ;
144
+
145
+ let new_path = if let Some ( best_path) = best_path {
146
+ select_best_path ( best_path, path, prefer_no_std)
147
+ } else {
148
+ path
149
+ } ;
150
+ best_path_len = new_path. len ( ) ;
151
+ best_path = Some ( new_path) ;
152
+ }
153
+ }
154
+ } else {
155
+ // Item was defined in some upstream crate. This means that it must be exported from one,
156
+ // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
157
+ // that wants to import it here, but we always prefer to use the external path here.
158
+
159
+ let crate_graph = db. crate_graph ( ) ;
160
+ let extern_paths = crate_graph[ from. krate ] . dependencies . iter ( ) . filter_map ( |dep| {
161
+ let import_map = db. import_map ( dep. crate_id ) ;
162
+ import_map. path_of ( item) . map ( |modpath| {
163
+ let mut modpath = modpath. clone ( ) ;
164
+ modpath. segments . insert ( 0 , dep. as_name ( ) ) ;
165
+ modpath
166
+ } )
167
+ } ) ;
168
+
169
+ for path in extern_paths {
170
+ let new_path = if let Some ( best_path) = best_path {
171
+ select_best_path ( best_path, path, prefer_no_std)
172
+ } else {
173
+ path
174
+ } ;
175
+ best_path = Some ( new_path) ;
176
+ }
160
177
}
178
+
161
179
best_path
162
180
}
163
181
@@ -185,69 +203,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
185
203
}
186
204
}
187
205
188
- fn find_importable_locations (
206
+ /// Finds locations in `from.krate` from which `item` can be imported by `from`.
207
+ fn find_local_import_locations (
189
208
db : & dyn DefDatabase ,
190
209
item : ItemInNs ,
191
210
from : ModuleId ,
192
211
) -> Vec < ( ModuleId , Name ) > {
193
- let crate_graph = db. crate_graph ( ) ;
194
- let mut result = Vec :: new ( ) ;
195
- // We only look in the crate from which we are importing, and the direct
196
- // dependencies. We cannot refer to names from transitive dependencies
197
- // directly (only through reexports in direct dependencies).
198
- for krate in Some ( from. krate )
199
- . into_iter ( )
200
- . chain ( crate_graph[ from. krate ] . dependencies . iter ( ) . map ( |dep| dep. crate_id ) )
201
- {
202
- result. extend (
203
- db. importable_locations_of ( item, krate)
204
- . iter ( )
205
- . filter ( |( _, _, vis) | vis. is_visible_from ( db, from) )
206
- . map ( |( m, n, _) | ( * m, n. clone ( ) ) ) ,
207
- ) ;
208
- }
209
- result
210
- }
212
+ let _p = profile ( "find_local_import_locations" ) ;
213
+
214
+ // `from` can import anything below `from` with visibility of at least `from`, and anything
215
+ // above `from` with any visibility. That means we do not need to descend into private siblings
216
+ // of `from` (and similar).
217
+
218
+ let def_map = db. crate_def_map ( from. krate ) ;
219
+
220
+ // Compute the initial worklist. We start with all direct child modules of `from` as well as all
221
+ // of its (recursive) parent modules.
222
+ let data = & def_map. modules [ from. local_id ] ;
223
+ let mut worklist = data
224
+ . children
225
+ . values ( )
226
+ . map ( |child| ModuleId { krate : from. krate , local_id : * child } )
227
+ . collect :: < Vec < _ > > ( ) ;
228
+ let mut parent = data. parent ;
229
+ while let Some ( p) = parent {
230
+ worklist. push ( ModuleId { krate : from. krate , local_id : p } ) ;
231
+ parent = def_map. modules [ p] . parent ;
232
+ }
233
+
234
+ let mut seen: FxHashSet < _ > = FxHashSet :: default ( ) ;
235
+
236
+ let mut locations = Vec :: new ( ) ;
237
+ while let Some ( module) = worklist. pop ( ) {
238
+ if !seen. insert ( module) {
239
+ continue ; // already processed this module
240
+ }
241
+
242
+ let ext_def_map;
243
+ let data = if module. krate == from. krate {
244
+ & def_map[ module. local_id ]
245
+ } else {
246
+ // The crate might reexport a module defined in another crate.
247
+ ext_def_map = db. crate_def_map ( module. krate ) ;
248
+ & ext_def_map[ module. local_id ]
249
+ } ;
211
250
212
- /// Collects all locations from which we might import the item in a particular
213
- /// crate. These include the original definition of the item, and any
214
- /// non-private `use`s.
215
- ///
216
- /// Note that the crate doesn't need to be the one in which the item is defined;
217
- /// it might be re-exported in other crates.
218
- pub ( crate ) fn importable_locations_of_query (
219
- db : & dyn DefDatabase ,
220
- item : ItemInNs ,
221
- krate : CrateId ,
222
- ) -> Arc < [ ( ModuleId , Name , Visibility ) ] > {
223
- let _p = profile ( "importable_locations_of_query" ) ;
224
- let def_map = db. crate_def_map ( krate) ;
225
- let mut result = Vec :: new ( ) ;
226
- for ( local_id, data) in def_map. modules . iter ( ) {
227
251
if let Some ( ( name, vis) ) = data. scope . name_of ( item) {
228
- let is_private = if let Visibility :: Module ( private_to) = vis {
229
- private_to. local_id == local_id
230
- } else {
231
- false
232
- } ;
233
- let is_original_def = if let Some ( module_def_id) = item. as_module_def_id ( ) {
234
- data. scope . declarations ( ) . any ( |it| it == module_def_id)
235
- } else {
236
- false
237
- } ;
238
- if is_private && !is_original_def {
252
+ if vis. is_visible_from ( db, from) {
253
+ let is_private = if let Visibility :: Module ( private_to) = vis {
254
+ private_to. local_id == module. local_id
255
+ } else {
256
+ false
257
+ } ;
258
+ let is_original_def = if let Some ( module_def_id) = item. as_module_def_id ( ) {
259
+ data. scope . declarations ( ) . any ( |it| it == module_def_id)
260
+ } else {
261
+ false
262
+ } ;
263
+
239
264
// Ignore private imports. these could be used if we are
240
265
// in a submodule of this module, but that's usually not
241
266
// what the user wants; and if this module can import
242
267
// the item and we're a submodule of it, so can we.
243
268
// Also this keeps the cached data smaller.
244
- continue ;
269
+ if !is_private || is_original_def {
270
+ locations. push ( ( module, name. clone ( ) ) ) ;
271
+ }
272
+ }
273
+ }
274
+
275
+ // Descend into all modules visible from `from`.
276
+ for ( _, per_ns) in data. scope . entries ( ) {
277
+ if let Some ( ( ModuleDefId :: ModuleId ( module) , vis) ) = per_ns. take_types_vis ( ) {
278
+ if vis. is_visible_from ( db, from) {
279
+ worklist. push ( module) ;
280
+ }
245
281
}
246
- result. push ( ( ModuleId { krate, local_id } , name. clone ( ) , vis) ) ;
247
282
}
248
283
}
249
284
250
- Arc :: from ( result )
285
+ locations
251
286
}
252
287
253
288
#[ cfg( test) ]
@@ -385,14 +420,15 @@ mod tests {
385
420
386
421
#[ test]
387
422
fn different_crate_renamed ( ) {
423
+ // Even if a local path exists, if the item is defined externally, prefer an external path.
388
424
let code = r#"
389
425
//- /main.rs crate:main deps:std
390
426
extern crate std as std_renamed;
391
427
<|>
392
428
//- /std.rs crate:std
393
429
pub struct S;
394
430
"# ;
395
- check_found_path ( code, "std_renamed ::S" ) ;
431
+ check_found_path ( code, "std ::S" ) ;
396
432
}
397
433
398
434
#[ test]
0 commit comments