3
3
use std:: { collections:: hash_map:: Entry , fmt, hash:: BuildHasherDefault } ;
4
4
5
5
use base_db:: CrateId ;
6
- use fst:: { self , Streamer } ;
6
+ use fst:: { self , raw :: IndexedValue , Streamer } ;
7
7
use hir_expand:: name:: Name ;
8
8
use indexmap:: IndexMap ;
9
9
use itertools:: Itertools ;
10
10
use rustc_hash:: { FxHashMap , FxHashSet , FxHasher } ;
11
+ use smallvec:: { smallvec, SmallVec } ;
11
12
use triomphe:: Arc ;
12
13
13
14
use crate :: {
@@ -20,31 +21,28 @@ use crate::{
20
21
21
22
type FxIndexMap < K , V > = IndexMap < K , V , BuildHasherDefault < FxHasher > > ;
22
23
23
- // FIXME: Support aliases: an item may be exported under multiple names, so `ImportInfo` should
24
- // have `Vec<(Name, ModuleId)>` instead of `(Name, ModuleId)`.
25
24
/// Item import details stored in the `ImportMap`.
26
25
#[ derive( Debug , Clone , Eq , PartialEq ) ]
27
26
pub struct ImportInfo {
28
27
/// A name that can be used to import the item, relative to the crate's root.
29
28
pub name : Name ,
30
29
/// The module containing this item.
31
30
pub container : ModuleId ,
32
- /// Whether the import is a trait associated item or not.
33
- pub is_trait_assoc_item : bool ,
34
31
/// Whether this item is annotated with `#[doc(hidden)]`.
35
32
pub is_doc_hidden : bool ,
36
33
/// Whether this item is annotated with `#[unstable(..)]`.
37
34
pub is_unstable : bool ,
38
35
}
39
36
37
+ type ImportMapIndex = FxIndexMap < ItemInNs , ( SmallVec < [ ImportInfo ; 1 ] > , IsTraitAssocItem ) > ;
38
+
40
39
/// A map from publicly exported items to its name.
41
40
///
42
41
/// Reexports of items are taken into account, ie. if something is exported under multiple
43
42
/// names, the one with the shortest import path will be used.
44
43
#[ derive( Default ) ]
45
44
pub struct ImportMap {
46
- map : FxIndexMap < ItemInNs , ImportInfo > ,
47
-
45
+ map : ImportMapIndex ,
48
46
/// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
49
47
/// values returned by running `fst`.
50
48
///
@@ -55,6 +53,12 @@ pub struct ImportMap {
55
53
fst : fst:: Map < Vec < u8 > > ,
56
54
}
57
55
56
+ #[ derive( Copy , Clone , PartialEq , Eq ) ]
57
+ enum IsTraitAssocItem {
58
+ Yes ,
59
+ No ,
60
+ }
61
+
58
62
impl ImportMap {
59
63
pub ( crate ) fn import_map_query ( db : & dyn DefDatabase , krate : CrateId ) -> Arc < Self > {
60
64
let _p = profile:: span ( "import_map_query" ) ;
@@ -64,9 +68,13 @@ impl ImportMap {
64
68
let mut importables: Vec < _ > = map
65
69
. iter ( )
66
70
// We've only collected items, whose name cannot be tuple field.
67
- . map ( |( & item, info) | ( item, info. name . as_str ( ) . unwrap ( ) . to_ascii_lowercase ( ) ) )
71
+ . flat_map ( |( & item, ( info, _) ) | {
72
+ info. iter ( )
73
+ . map ( move |info| ( item, info. name . as_str ( ) . unwrap ( ) . to_ascii_lowercase ( ) ) )
74
+ } )
68
75
. collect ( ) ;
69
76
importables. sort_by ( |( _, lhs_name) , ( _, rhs_name) | lhs_name. cmp ( rhs_name) ) ;
77
+ importables. dedup ( ) ;
70
78
71
79
// Build the FST, taking care not to insert duplicate values.
72
80
let mut builder = fst:: MapBuilder :: memory ( ) ;
@@ -82,12 +90,12 @@ impl ImportMap {
82
90
} )
83
91
}
84
92
85
- pub fn import_info_for ( & self , item : ItemInNs ) -> Option < & ImportInfo > {
86
- self . map . get ( & item)
93
+ pub fn import_info_for ( & self , item : ItemInNs ) -> Option < & [ ImportInfo ] > {
94
+ self . map . get ( & item) . map ( | ( info , _ ) | & * * info )
87
95
}
88
96
}
89
97
90
- fn collect_import_map ( db : & dyn DefDatabase , krate : CrateId ) -> FxIndexMap < ItemInNs , ImportInfo > {
98
+ fn collect_import_map ( db : & dyn DefDatabase , krate : CrateId ) -> ImportMapIndex {
91
99
let _p = profile:: span ( "collect_import_map" ) ;
92
100
93
101
let def_map = db. crate_def_map ( krate) ;
@@ -140,7 +148,6 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
140
148
let import_info = ImportInfo {
141
149
name : name. clone ( ) ,
142
150
container : module,
143
- is_trait_assoc_item : false ,
144
151
is_doc_hidden,
145
152
is_unstable,
146
153
} ;
@@ -180,6 +187,8 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
180
187
depth < occ_depth
181
188
}
182
189
} ;
190
+ // FIXME: Remove the overwrite rules as we can now record exports and
191
+ // aliases for the same item
183
192
if !overwrite {
184
193
continue ;
185
194
}
@@ -197,7 +206,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
197
206
) ;
198
207
}
199
208
200
- map. insert ( item, import_info) ;
209
+ map. insert ( item, ( smallvec ! [ import_info] , IsTraitAssocItem :: No ) ) ;
201
210
202
211
// If we've just added a module, descend into it. We might traverse modules
203
212
// multiple times, but only if the module depth is smaller (else we `continue`
@@ -214,7 +223,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
214
223
215
224
fn collect_trait_assoc_items (
216
225
db : & dyn DefDatabase ,
217
- map : & mut FxIndexMap < ItemInNs , ImportInfo > ,
226
+ map : & mut ImportMapIndex ,
218
227
tr : TraitId ,
219
228
is_type_in_ns : bool ,
220
229
trait_import_info : & ImportInfo ,
@@ -241,11 +250,10 @@ fn collect_trait_assoc_items(
241
250
let assoc_item_info = ImportInfo {
242
251
container : trait_import_info. container ,
243
252
name : assoc_item_name. clone ( ) ,
244
- is_trait_assoc_item : true ,
245
253
is_doc_hidden : attrs. has_doc_hidden ( ) ,
246
254
is_unstable : attrs. is_unstable ( ) ,
247
255
} ;
248
- map. insert ( assoc_item, assoc_item_info) ;
256
+ map. insert ( assoc_item, ( smallvec ! [ assoc_item_info] , IsTraitAssocItem :: Yes ) ) ;
249
257
}
250
258
}
251
259
@@ -349,19 +357,24 @@ impl Query {
349
357
Self { case_sensitive : true , ..self }
350
358
}
351
359
360
+ fn matches_assoc_mode ( & self , is_trait_assoc_item : IsTraitAssocItem ) -> bool {
361
+ match ( is_trait_assoc_item, self . assoc_mode ) {
362
+ ( IsTraitAssocItem :: Yes , AssocSearchMode :: Exclude )
363
+ | ( IsTraitAssocItem :: No , AssocSearchMode :: AssocItemsOnly ) => false ,
364
+ _ => true ,
365
+ }
366
+ }
367
+
368
+ /// Checks whether the import map entry matches the query.
352
369
fn import_matches (
353
370
& self ,
354
371
db : & dyn DefDatabase ,
355
372
import : & ImportInfo ,
356
373
enforce_lowercase : bool ,
357
374
) -> bool {
358
375
let _p = profile:: span ( "import_map::Query::import_matches" ) ;
359
- match ( import. is_trait_assoc_item , self . assoc_mode ) {
360
- ( true , AssocSearchMode :: Exclude ) => return false ,
361
- ( false , AssocSearchMode :: AssocItemsOnly ) => return false ,
362
- _ => { }
363
- }
364
376
377
+ // FIXME: Can we get rid of the alloc here?
365
378
let mut input = import. name . display ( db. upcast ( ) ) . to_string ( ) ;
366
379
let case_insensitive = enforce_lowercase || !self . case_sensitive ;
367
380
if case_insensitive {
@@ -411,32 +424,55 @@ pub fn search_dependencies(
411
424
412
425
let mut res = FxHashSet :: default ( ) ;
413
426
while let Some ( ( _, indexed_values) ) = stream. next ( ) {
414
- for indexed_value in indexed_values {
415
- let import_map = & import_maps[ indexed_value. index ] ;
416
- let importables = & import_map. importables [ indexed_value. value as usize ..] ;
427
+ for & IndexedValue { index, value } in indexed_values {
428
+ let import_map = & import_maps[ index] ;
429
+ let [ importable, importables @ ..] = & import_map. importables [ value as usize ..] else {
430
+ continue ;
431
+ } ;
417
432
418
- let common_importable_data = & import_map. map [ & importables [ 0 ] ] ;
419
- if !query. import_matches ( db , common_importable_data , true ) {
433
+ let & ( ref importable_data , is_trait_assoc_item ) = & import_map. map [ importable ] ;
434
+ if !query. matches_assoc_mode ( is_trait_assoc_item ) {
420
435
continue ;
421
436
}
422
437
423
- // Name shared by the importable items in this group.
424
- let common_importable_name =
425
- common_importable_data. name . to_smol_str ( ) . to_ascii_lowercase ( ) ;
426
- // Add the items from this name group. Those are all subsequent items in
427
- // `importables` whose name match `common_importable_name`.
428
- let iter = importables
429
- . iter ( )
430
- . copied ( )
431
- . take_while ( |item| {
432
- common_importable_name
433
- == import_map. map [ item] . name . to_smol_str ( ) . to_ascii_lowercase ( )
434
- } )
435
- . filter ( |item| {
436
- !query. case_sensitive // we've already checked the common importables name case-insensitively
437
- || query. import_matches ( db, & import_map. map [ item] , false )
438
- } ) ;
439
- res. extend ( iter) ;
438
+ // FIXME: We probably need to account for other possible matches in this alias group?
439
+ let Some ( common_importable_data) =
440
+ importable_data. iter ( ) . find ( |& info| query. import_matches ( db, info, true ) )
441
+ else {
442
+ continue ;
443
+ } ;
444
+ res. insert ( * importable) ;
445
+
446
+ if !importables. is_empty ( ) {
447
+ // FIXME: so many allocs...
448
+ // Name shared by the importable items in this group.
449
+ let common_importable_name =
450
+ common_importable_data. name . to_smol_str ( ) . to_ascii_lowercase ( ) ;
451
+ // Add the items from this name group. Those are all subsequent items in
452
+ // `importables` whose name match `common_importable_name`.
453
+ let iter = importables
454
+ . iter ( )
455
+ . copied ( )
456
+ . take_while ( |item| {
457
+ let & ( ref import_infos, assoc_mode) = & import_map. map [ item] ;
458
+ query. matches_assoc_mode ( assoc_mode)
459
+ && import_infos. iter ( ) . any ( |info| {
460
+ info. name . to_smol_str ( ) . to_ascii_lowercase ( )
461
+ == common_importable_name
462
+ } )
463
+ } )
464
+ . filter ( |item| {
465
+ !query. case_sensitive || {
466
+ // we've already checked the common importables name case-insensitively
467
+ let & ( ref import_infos, assoc_mode) = & import_map. map [ item] ;
468
+ query. matches_assoc_mode ( assoc_mode)
469
+ && import_infos
470
+ . iter ( )
471
+ . any ( |info| query. import_matches ( db, info, false ) )
472
+ }
473
+ } ) ;
474
+ res. extend ( iter) ;
475
+ }
440
476
441
477
if res. len ( ) >= query. limit {
442
478
return res;
@@ -461,6 +497,7 @@ mod tests {
461
497
let mut importable_paths: Vec < _ > = self
462
498
. map
463
499
. iter ( )
500
+ . flat_map ( |( item, ( info, _) ) | info. iter ( ) . map ( move |info| ( item, info) ) )
464
501
. map ( |( item, info) | {
465
502
let path = render_path ( db, info) ;
466
503
let ns = match item {
@@ -499,7 +536,7 @@ mod tests {
499
536
let ( path, mark) = match assoc_item_path ( & db, & dependency_imports, dependency) {
500
537
Some ( assoc_item_path) => ( assoc_item_path, "a" ) ,
501
538
None => (
502
- render_path ( & db, dependency_imports. import_info_for ( dependency) ?) ,
539
+ render_path ( & db, & dependency_imports. import_info_for ( dependency) ?[ 0 ] ) ,
503
540
match dependency {
504
541
ItemInNs :: Types ( ModuleDefId :: FunctionId ( _) )
505
542
| ItemInNs :: Values ( ModuleDefId :: FunctionId ( _) ) => "f" ,
@@ -547,7 +584,12 @@ mod tests {
547
584
. items
548
585
. iter ( )
549
586
. find ( |( _, assoc_item_id) | & dependency_assoc_item_id == assoc_item_id) ?;
550
- Some ( format ! ( "{}::{}" , render_path( db, trait_info) , assoc_item_name. display( db. upcast( ) ) ) )
587
+ // FIXME: This should check all import infos, not just the first
588
+ Some ( format ! (
589
+ "{}::{}" ,
590
+ render_path( db, & trait_info[ 0 ] ) ,
591
+ assoc_item_name. display( db. upcast( ) )
592
+ ) )
551
593
}
552
594
553
595
fn check ( ra_fixture : & str , expect : Expect ) {
0 commit comments