33use std:: { collections:: hash_map:: Entry , fmt, hash:: BuildHasherDefault } ;
44
55use base_db:: CrateId ;
6- use fst:: { self , Streamer } ;
6+ use fst:: { self , raw :: IndexedValue , Streamer } ;
77use hir_expand:: name:: Name ;
88use indexmap:: IndexMap ;
99use itertools:: Itertools ;
1010use rustc_hash:: { FxHashMap , FxHashSet , FxHasher } ;
11+ use smallvec:: { smallvec, SmallVec } ;
1112use triomphe:: Arc ;
1213
1314use crate :: {
@@ -20,31 +21,28 @@ use crate::{
2021
2122type FxIndexMap < K , V > = IndexMap < K , V , BuildHasherDefault < FxHasher > > ;
2223
23- // FIXME: Support aliases: an item may be exported under multiple names, so `ImportInfo` should
24- // have `Vec<(Name, ModuleId)>` instead of `(Name, ModuleId)`.
2524/// Item import details stored in the `ImportMap`.
2625#[ derive( Debug , Clone , Eq , PartialEq ) ]
2726pub struct ImportInfo {
2827 /// A name that can be used to import the item, relative to the crate's root.
2928 pub name : Name ,
3029 /// The module containing this item.
3130 pub container : ModuleId ,
32- /// Whether the import is a trait associated item or not.
33- pub is_trait_assoc_item : bool ,
3431 /// Whether this item is annotated with `#[doc(hidden)]`.
3532 pub is_doc_hidden : bool ,
3633 /// Whether this item is annotated with `#[unstable(..)]`.
3734 pub is_unstable : bool ,
3835}
3936
37+ type ImportMapIndex = FxIndexMap < ItemInNs , ( SmallVec < [ ImportInfo ; 1 ] > , IsTraitAssocItem ) > ;
38+
4039/// A map from publicly exported items to its name.
4140///
4241/// Reexports of items are taken into account, ie. if something is exported under multiple
4342/// names, the one with the shortest import path will be used.
4443#[ derive( Default ) ]
4544pub struct ImportMap {
46- map : FxIndexMap < ItemInNs , ImportInfo > ,
47-
45+ map : ImportMapIndex ,
4846 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
4947 /// values returned by running `fst`.
5048 ///
@@ -55,6 +53,12 @@ pub struct ImportMap {
5553 fst : fst:: Map < Vec < u8 > > ,
5654}
5755
56+ #[ derive( Copy , Clone , PartialEq , Eq ) ]
57+ enum IsTraitAssocItem {
58+ Yes ,
59+ No ,
60+ }
61+
5862impl ImportMap {
5963 pub ( crate ) fn import_map_query ( db : & dyn DefDatabase , krate : CrateId ) -> Arc < Self > {
6064 let _p = profile:: span ( "import_map_query" ) ;
@@ -64,9 +68,13 @@ impl ImportMap {
6468 let mut importables: Vec < _ > = map
6569 . iter ( )
6670 // 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+ } )
6875 . collect ( ) ;
6976 importables. sort_by ( |( _, lhs_name) , ( _, rhs_name) | lhs_name. cmp ( rhs_name) ) ;
77+ importables. dedup ( ) ;
7078
7179 // Build the FST, taking care not to insert duplicate values.
7280 let mut builder = fst:: MapBuilder :: memory ( ) ;
@@ -82,12 +90,12 @@ impl ImportMap {
8290 } )
8391 }
8492
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 )
8795 }
8896}
8997
90- fn collect_import_map ( db : & dyn DefDatabase , krate : CrateId ) -> FxIndexMap < ItemInNs , ImportInfo > {
98+ fn collect_import_map ( db : & dyn DefDatabase , krate : CrateId ) -> ImportMapIndex {
9199 let _p = profile:: span ( "collect_import_map" ) ;
92100
93101 let def_map = db. crate_def_map ( krate) ;
@@ -140,7 +148,6 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
140148 let import_info = ImportInfo {
141149 name : name. clone ( ) ,
142150 container : module,
143- is_trait_assoc_item : false ,
144151 is_doc_hidden,
145152 is_unstable,
146153 } ;
@@ -180,6 +187,8 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
180187 depth < occ_depth
181188 }
182189 } ;
190+ // FIXME: Remove the overwrite rules as we can now record exports and
191+ // aliases for the same item
183192 if !overwrite {
184193 continue ;
185194 }
@@ -197,7 +206,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
197206 ) ;
198207 }
199208
200- map. insert ( item, import_info) ;
209+ map. insert ( item, ( smallvec ! [ import_info] , IsTraitAssocItem :: No ) ) ;
201210
202211 // If we've just added a module, descend into it. We might traverse modules
203212 // 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
214223
215224fn collect_trait_assoc_items (
216225 db : & dyn DefDatabase ,
217- map : & mut FxIndexMap < ItemInNs , ImportInfo > ,
226+ map : & mut ImportMapIndex ,
218227 tr : TraitId ,
219228 is_type_in_ns : bool ,
220229 trait_import_info : & ImportInfo ,
@@ -241,11 +250,10 @@ fn collect_trait_assoc_items(
241250 let assoc_item_info = ImportInfo {
242251 container : trait_import_info. container ,
243252 name : assoc_item_name. clone ( ) ,
244- is_trait_assoc_item : true ,
245253 is_doc_hidden : attrs. has_doc_hidden ( ) ,
246254 is_unstable : attrs. is_unstable ( ) ,
247255 } ;
248- map. insert ( assoc_item, assoc_item_info) ;
256+ map. insert ( assoc_item, ( smallvec ! [ assoc_item_info] , IsTraitAssocItem :: Yes ) ) ;
249257 }
250258}
251259
@@ -349,19 +357,24 @@ impl Query {
349357 Self { case_sensitive : true , ..self }
350358 }
351359
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.
352369 fn import_matches (
353370 & self ,
354371 db : & dyn DefDatabase ,
355372 import : & ImportInfo ,
356373 enforce_lowercase : bool ,
357374 ) -> bool {
358375 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- }
364376
377+ // FIXME: Can we get rid of the alloc here?
365378 let mut input = import. name . display ( db. upcast ( ) ) . to_string ( ) ;
366379 let case_insensitive = enforce_lowercase || !self . case_sensitive ;
367380 if case_insensitive {
@@ -411,32 +424,55 @@ pub fn search_dependencies(
411424
412425 let mut res = FxHashSet :: default ( ) ;
413426 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+ } ;
417432
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 ) {
420435 continue ;
421436 }
422437
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+ }
440476
441477 if res. len ( ) >= query. limit {
442478 return res;
@@ -461,6 +497,7 @@ mod tests {
461497 let mut importable_paths: Vec < _ > = self
462498 . map
463499 . iter ( )
500+ . flat_map ( |( item, ( info, _) ) | info. iter ( ) . map ( move |info| ( item, info) ) )
464501 . map ( |( item, info) | {
465502 let path = render_path ( db, info) ;
466503 let ns = match item {
@@ -499,7 +536,7 @@ mod tests {
499536 let ( path, mark) = match assoc_item_path ( & db, & dependency_imports, dependency) {
500537 Some ( assoc_item_path) => ( assoc_item_path, "a" ) ,
501538 None => (
502- render_path ( & db, dependency_imports. import_info_for ( dependency) ?) ,
539+ render_path ( & db, & dependency_imports. import_info_for ( dependency) ?[ 0 ] ) ,
503540 match dependency {
504541 ItemInNs :: Types ( ModuleDefId :: FunctionId ( _) )
505542 | ItemInNs :: Values ( ModuleDefId :: FunctionId ( _) ) => "f" ,
@@ -547,7 +584,12 @@ mod tests {
547584 . items
548585 . iter ( )
549586 . 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+ ) )
551593 }
552594
553595 fn check ( ra_fixture : & str , expect : Expect ) {
0 commit comments