1
1
//! Look up accessible paths for items.
2
2
use either:: Either ;
3
3
use hir:: {
4
- AsAssocItem , AssocItem , Crate , ItemInNs , MacroDef , ModPath , Module , ModuleDef , PrefixKind ,
5
- Semantics ,
4
+ AsAssocItem , AssocItem , Crate , ItemInNs , MacroDef , ModPath , Module , ModuleDef , Name ,
5
+ PrefixKind , Semantics ,
6
6
} ;
7
7
use rustc_hash:: FxHashSet ;
8
8
use syntax:: { ast, AstNode } ;
@@ -34,10 +34,16 @@ pub struct TraitImportCandidate {
34
34
35
35
#[ derive( Debug ) ]
36
36
pub struct PathImportCandidate {
37
- pub unresolved_qualifier : Option < ast :: Path > ,
37
+ pub qualifier : Qualifier ,
38
38
pub name : NameToImport ,
39
39
}
40
40
41
+ #[ derive( Debug ) ]
42
+ pub enum Qualifier {
43
+ Absent ,
44
+ FirstSegmentUnresolved ( ast:: PathSegment , ast:: Path ) ,
45
+ }
46
+
41
47
#[ derive( Debug ) ]
42
48
pub enum NameToImport {
43
49
Exact ( String ) ,
@@ -162,8 +168,9 @@ impl ImportAssets {
162
168
let ( assoc_item_search, limit) = if self . import_candidate . is_trait_candidate ( ) {
163
169
( AssocItemSearch :: AssocItemsOnly , None )
164
170
} else {
165
- ( AssocItemSearch :: Exclude , Some ( DEFAULT_QUERY_SEARCH_LIMIT ) )
171
+ ( AssocItemSearch :: Include , Some ( DEFAULT_QUERY_SEARCH_LIMIT ) )
166
172
} ;
173
+
167
174
imports_locator:: find_similar_imports (
168
175
sema,
169
176
current_crate,
@@ -192,17 +199,16 @@ impl ImportAssets {
192
199
let db = sema. db ;
193
200
194
201
match & self . import_candidate {
195
- ImportCandidate :: Path ( path_candidate) => Box :: new ( path_applicable_items (
196
- sema,
197
- path_candidate,
198
- unfiltered_defs
199
- . into_iter ( )
200
- . map ( |def| def. either ( ItemInNs :: from, ItemInNs :: from) )
201
- . filter_map ( move |item_to_search| {
202
- get_mod_path ( db, item_to_search, & self . module_with_candidate , prefixed)
203
- . zip ( Some ( item_to_search) )
204
- } ) ,
205
- ) ) ,
202
+ ImportCandidate :: Path ( path_candidate) => Box :: new (
203
+ path_applicable_items (
204
+ db,
205
+ path_candidate,
206
+ & self . module_with_candidate ,
207
+ prefixed,
208
+ unfiltered_defs,
209
+ )
210
+ . into_iter ( ) ,
211
+ ) ,
206
212
ImportCandidate :: TraitAssocItem ( trait_candidate) => Box :: new (
207
213
trait_applicable_defs ( db, current_crate, trait_candidate, true , unfiltered_defs)
208
214
. into_iter ( )
@@ -224,27 +230,110 @@ impl ImportAssets {
224
230
}
225
231
226
232
fn path_applicable_items < ' a > (
227
- sema : & ' a Semantics < RootDatabase > ,
228
- path_candidate : & PathImportCandidate ,
229
- unfiltered_defs : impl Iterator < Item = ( ModPath , ItemInNs ) > + ' a ,
230
- ) -> Box < dyn Iterator < Item = ( ModPath , ItemInNs ) > + ' a > {
231
- let unresolved_qualifier = match & path_candidate. unresolved_qualifier {
232
- Some ( qualifier) => qualifier,
233
- None => {
234
- return Box :: new ( unfiltered_defs) ;
233
+ db : & ' a RootDatabase ,
234
+ path_candidate : & ' a PathImportCandidate ,
235
+ module_with_candidate : & hir:: Module ,
236
+ prefixed : Option < hir:: PrefixKind > ,
237
+ unfiltered_defs : impl Iterator < Item = Either < ModuleDef , MacroDef > > + ' a ,
238
+ ) -> FxHashSet < ( ModPath , ItemInNs ) > {
239
+ let applicable_items = unfiltered_defs
240
+ . filter_map ( |def| {
241
+ let ( assoc_original, candidate) = match def {
242
+ Either :: Left ( module_def) => match module_def. as_assoc_item ( db) {
243
+ Some ( assoc_item) => match assoc_item. container ( db) {
244
+ hir:: AssocItemContainer :: Trait ( trait_) => {
245
+ ( Some ( module_def) , ItemInNs :: from ( ModuleDef :: from ( trait_) ) )
246
+ }
247
+ hir:: AssocItemContainer :: Impl ( impl_) => (
248
+ Some ( module_def) ,
249
+ ItemInNs :: from ( ModuleDef :: from ( impl_. target_ty ( db) . as_adt ( ) ?) ) ,
250
+ ) ,
251
+ } ,
252
+ None => ( None , ItemInNs :: from ( module_def) ) ,
253
+ } ,
254
+ Either :: Right ( macro_def) => ( None , ItemInNs :: from ( macro_def) ) ,
255
+ } ;
256
+ Some ( ( assoc_original, candidate) )
257
+ } )
258
+ . filter_map ( |( assoc_original, candidate) | {
259
+ get_mod_path ( db, candidate, module_with_candidate, prefixed)
260
+ . zip ( Some ( ( assoc_original, candidate) ) )
261
+ } ) ;
262
+
263
+ let ( unresolved_first_segment, unresolved_qualifier) = match & path_candidate. qualifier {
264
+ Qualifier :: Absent => {
265
+ return applicable_items
266
+ . map ( |( candidate_path, ( _, candidate) ) | ( candidate_path, candidate) )
267
+ . collect ( ) ;
235
268
}
269
+ Qualifier :: FirstSegmentUnresolved ( first_segment, qualifier) => ( first_segment, qualifier) ,
236
270
} ;
237
271
238
- let qualifier_string = unresolved_qualifier. to_string ( ) ;
239
- Box :: new ( unfiltered_defs. filter ( move |( candidate_path, _) | {
240
- let mut candidate_qualifier = candidate_path. clone ( ) ;
241
- candidate_qualifier. pop_segment ( ) ;
272
+ // TODO kb need to remove turbofish from the qualifier, maybe use the segments instead?
273
+ let unresolved_qualifier_string = unresolved_qualifier. to_string ( ) ;
274
+ let unresolved_first_segment_string = unresolved_first_segment. to_string ( ) ;
275
+
276
+ applicable_items
277
+ . filter ( |( candidate_path, _) | {
278
+ let candidate_path_string = candidate_path. to_string ( ) ;
279
+ candidate_path_string. contains ( & unresolved_qualifier_string)
280
+ && candidate_path_string. contains ( & unresolved_first_segment_string)
281
+ } )
282
+ // TODO kb need to adjust the return type: I get the results rendered rather badly
283
+ . filter_map ( |( candidate_path, ( assoc_original, candidate) ) | {
284
+ if let Some ( assoc_original) = assoc_original {
285
+ if item_name ( db, candidate) ?. to_string ( ) == unresolved_first_segment_string {
286
+ return Some ( ( candidate_path, ItemInNs :: from ( assoc_original) ) ) ;
287
+ }
288
+ }
289
+
290
+ let matching_module =
291
+ module_with_matching_name ( db, & unresolved_first_segment_string, candidate) ?;
292
+ let path = get_mod_path (
293
+ db,
294
+ ItemInNs :: from ( ModuleDef :: from ( matching_module) ) ,
295
+ module_with_candidate,
296
+ prefixed,
297
+ ) ?;
298
+ Some ( ( path, candidate) )
299
+ } )
300
+ . collect ( )
301
+ }
302
+
303
+ fn item_name ( db : & RootDatabase , item : ItemInNs ) -> Option < Name > {
304
+ match item {
305
+ ItemInNs :: Types ( module_def_id) => ModuleDef :: from ( module_def_id) . name ( db) ,
306
+ ItemInNs :: Values ( module_def_id) => ModuleDef :: from ( module_def_id) . name ( db) ,
307
+ ItemInNs :: Macros ( macro_def_id) => MacroDef :: from ( macro_def_id) . name ( db) ,
308
+ }
309
+ }
242
310
243
- // TODO kb
244
- // * take 1st segment of `unresolved_qualifier` and return it instead of the original `ItemInNs`
245
- // * Update `ModPath`: pop until 1st segment of `unresolved_qualifier` reached (do not rely on name comparison, nested mod names can repeat)
246
- candidate_qualifier. to_string ( ) . ends_with ( & qualifier_string)
247
- } ) )
311
+ fn item_module ( db : & RootDatabase , item : ItemInNs ) -> Option < Module > {
312
+ match item {
313
+ ItemInNs :: Types ( module_def_id) => ModuleDef :: from ( module_def_id) . module ( db) ,
314
+ ItemInNs :: Values ( module_def_id) => ModuleDef :: from ( module_def_id) . module ( db) ,
315
+ ItemInNs :: Macros ( macro_def_id) => MacroDef :: from ( macro_def_id) . module ( db) ,
316
+ }
317
+ }
318
+
319
+ fn module_with_matching_name (
320
+ db : & RootDatabase ,
321
+ unresolved_first_segment_string : & str ,
322
+ candidate : ItemInNs ,
323
+ ) -> Option < Module > {
324
+ let mut current_module = item_module ( db, candidate) ;
325
+ while let Some ( module) = current_module {
326
+ match module. name ( db) {
327
+ Some ( module_name) => {
328
+ if module_name. to_string ( ) . as_str ( ) == unresolved_first_segment_string {
329
+ return Some ( module) ;
330
+ }
331
+ }
332
+ None => { }
333
+ }
334
+ current_module = module. parent ( db) ;
335
+ }
336
+ None
248
337
}
249
338
250
339
fn trait_applicable_defs < ' a > (
@@ -367,10 +456,20 @@ fn path_import_candidate(
367
456
) -> Option < ImportCandidate > {
368
457
Some ( match qualifier {
369
458
Some ( qualifier) => match sema. resolve_path ( & qualifier) {
370
- None => ImportCandidate :: Path ( PathImportCandidate {
371
- unresolved_qualifier : Some ( qualifier) ,
372
- name,
373
- } ) ,
459
+ None => {
460
+ let qualifier_start =
461
+ qualifier. syntax ( ) . descendants ( ) . find_map ( ast:: PathSegment :: cast) ?;
462
+ let qualifier_start_path =
463
+ qualifier_start. syntax ( ) . ancestors ( ) . find_map ( ast:: Path :: cast) ?;
464
+ if sema. resolve_path ( & qualifier_start_path) . is_none ( ) {
465
+ ImportCandidate :: Path ( PathImportCandidate {
466
+ qualifier : Qualifier :: FirstSegmentUnresolved ( qualifier_start, qualifier) ,
467
+ name,
468
+ } )
469
+ } else {
470
+ return None ;
471
+ }
472
+ }
374
473
Some ( hir:: PathResolution :: Def ( hir:: ModuleDef :: Adt ( assoc_item_path) ) ) => {
375
474
ImportCandidate :: TraitAssocItem ( TraitImportCandidate {
376
475
receiver_ty : assoc_item_path. ty ( sema. db ) ,
@@ -379,6 +478,6 @@ fn path_import_candidate(
379
478
}
380
479
Some ( _) => return None ,
381
480
} ,
382
- None => ImportCandidate :: Path ( PathImportCandidate { unresolved_qualifier : None , name } ) ,
481
+ None => ImportCandidate :: Path ( PathImportCandidate { qualifier : Qualifier :: Absent , name } ) ,
383
482
} )
384
483
}
0 commit comments