11//! An algorithm to find a path to refer to a certain item.
22
3- use std:: { cell:: Cell , cmp:: Ordering , iter, ops :: BitOr } ;
3+ use std:: { cell:: Cell , cmp:: Ordering , iter} ;
44
55use base_db:: { CrateId , CrateOrigin , LangCrateOrigin } ;
66use hir_expand:: {
@@ -13,7 +13,7 @@ use rustc_hash::FxHashSet;
1313use crate :: {
1414 db:: DefDatabase ,
1515 item_scope:: ItemInNs ,
16- nameres:: { DefMap , ModuleData } ,
16+ nameres:: DefMap ,
1717 path:: { ModPath , PathKind } ,
1818 visibility:: { Visibility , VisibilityExplicitness } ,
1919 ImportPathConfig , ModuleDefId , ModuleId ,
@@ -52,11 +52,11 @@ pub fn find_path(
5252 ignore_local_imports,
5353 from,
5454 from_def_map : & from. def_map ( db) ,
55- is_std_item : db. crate_graph ( ) [ item_module. krate ( ) ] . origin . is_lang ( ) ,
5655 fuel : Cell :: new ( FIND_PATH_FUEL ) ,
5756 } ,
5857 item,
5958 MAX_PATH_LEN ,
59+ db. crate_graph ( ) [ item_module. krate ( ) ] . origin . is_lang ( ) ,
6060 )
6161}
6262
@@ -67,13 +67,6 @@ enum Stability {
6767}
6868use Stability :: * ;
6969
70- fn zip_stability ( a : Stability , b : Stability ) -> Stability {
71- match ( a, b) {
72- ( Stable , Stable ) => Stable ,
73- _ => Unstable ,
74- }
75- }
76-
7770const MAX_PATH_LEN : usize = 15 ;
7871const FIND_PATH_FUEL : usize = 10000 ;
7972
@@ -107,17 +100,22 @@ struct FindPathCtx<'db> {
107100 ignore_local_imports : bool ,
108101 from : ModuleId ,
109102 from_def_map : & ' db DefMap ,
110- is_std_item : bool ,
111103 fuel : Cell < usize > ,
112104}
113105
114106/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
115- fn find_path_inner ( ctx : & FindPathCtx < ' _ > , item : ItemInNs , max_len : usize ) -> Option < ModPath > {
107+ fn find_path_inner (
108+ ctx : & FindPathCtx < ' _ > ,
109+ item : ItemInNs ,
110+ max_len : usize ,
111+ is_std_item : bool ,
112+ ) -> Option < ModPath > {
116113 // - if the item is a module, jump straight to module search
117- if let ItemInNs :: Types ( ModuleDefId :: ModuleId ( module_id) ) = item {
118- let mut visited_modules = FxHashSet :: default ( ) ;
119- return find_path_for_module ( ctx, & mut visited_modules, module_id, true , max_len)
120- . map ( |choice| choice. path ) ;
114+ if !is_std_item {
115+ if let ItemInNs :: Types ( ModuleDefId :: ModuleId ( module_id) ) = item {
116+ return find_path_for_module ( ctx, & mut FxHashSet :: default ( ) , module_id, true , max_len)
117+ . map ( |choice| choice. path ) ;
118+ }
121119 }
122120
123121 let may_be_in_scope = match ctx. prefix {
@@ -140,9 +138,12 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
140138
141139 if let Some ( ModuleDefId :: EnumVariantId ( variant) ) = item. as_module_def_id ( ) {
142140 // - if the item is an enum variant, refer to it via the enum
143- if let Some ( mut path) =
144- find_path_inner ( ctx, ItemInNs :: Types ( variant. lookup ( ctx. db ) . parent . into ( ) ) , max_len)
145- {
141+ if let Some ( mut path) = find_path_inner (
142+ ctx,
143+ ItemInNs :: Types ( variant. lookup ( ctx. db ) . parent . into ( ) ) ,
144+ max_len,
145+ is_std_item,
146+ ) {
146147 path. push_segment ( ctx. db . enum_variant_data ( variant) . name . clone ( ) ) ;
147148 return Some ( path) ;
148149 }
@@ -151,10 +152,18 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
151152 // variant somewhere
152153 }
153154
154- let mut visited_modules = FxHashSet :: default ( ) ;
155+ if is_std_item {
156+ // The item we are searching for comes from the sysroot libraries, so skip prefer looking in
157+ // the sysroot libraries directly.
158+ // We do need to fallback as the item in question could be re-exported by another crate
159+ // while not being a transitive dependency of the current crate.
160+ if let Some ( choice) = find_in_sysroot ( ctx, & mut FxHashSet :: default ( ) , item, max_len) {
161+ return Some ( choice. path ) ;
162+ }
163+ }
155164
156165 let mut best_choice = None ;
157- calculate_best_path ( ctx, & mut visited_modules , item, max_len, & mut best_choice) ;
166+ calculate_best_path ( ctx, & mut FxHashSet :: default ( ) , item, max_len, & mut best_choice) ;
158167 best_choice. map ( |choice| choice. path )
159168}
160169
@@ -178,7 +187,6 @@ fn find_path_for_module(
178187 path_text_len : 5 ,
179188 stability : Stable ,
180189 prefer_due_to_prelude : false ,
181- sysroot_score : 0 ,
182190 } ) ;
183191 }
184192 // - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude
@@ -241,7 +249,6 @@ fn find_path_for_module(
241249 path_text_len : path_kind_len ( kind) ,
242250 stability : Stable ,
243251 prefer_due_to_prelude : false ,
244- sysroot_score : 0 ,
245252 } ) ;
246253 }
247254 }
@@ -360,86 +367,97 @@ fn calculate_best_path(
360367 // dependency in this case.
361368 calculate_best_path_local ( ctx, visited_modules, item, max_len, best_choice)
362369 } else {
363- let db = ctx. db ;
364370 // Item was defined in some upstream crate. This means that it must be exported from one,
365371 // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
366372 // that wants to import it here, but we always prefer to use the external path here.
367373
368- let mut process_dep = |dep : CrateId , score| {
369- let import_map = db. import_map ( dep) ;
370- let Some ( import_info_for) = import_map. import_info_for ( item) else {
371- return false ;
372- } ;
373- let mut processed_something = false ;
374- for info in import_info_for {
375- if info. is_doc_hidden {
376- // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
377- continue ;
378- }
374+ ctx. db . crate_graph ( ) [ ctx. from . krate ] . dependencies . iter ( ) . for_each ( |dep| {
375+ find_in_dep ( ctx, visited_modules, item, max_len, best_choice, dep. crate_id )
376+ } ) ;
377+ }
378+ }
379379
380- // Determine best path for containing module and append last segment from `info`.
381- // FIXME: we should guide this to look up the path locally, or from the same crate again?
382- let choice = find_path_for_module (
383- ctx,
384- visited_modules,
385- info. container ,
386- true ,
387- best_choice. as_ref ( ) . map_or ( max_len, |it| it. path . len ( ) ) - 1 ,
388- ) ;
389- let Some ( mut choice) = choice else {
390- continue ;
391- } ;
392- choice. sysroot_score = score;
393- cov_mark:: hit!( partially_imported) ;
394- choice. stability = zip_stability (
395- choice. stability ,
396- if info. is_unstable { Unstable } else { Stable } ,
397- ) ;
398-
399- Choice :: try_select ( best_choice, choice, ctx. cfg . prefer_prelude , info. name . clone ( ) ) ;
400- processed_something = true ;
380+ fn find_in_sysroot (
381+ ctx : & FindPathCtx < ' _ > ,
382+ visited_modules : & mut FxHashSet < ( ItemInNs , ModuleId ) > ,
383+ item : ItemInNs ,
384+ max_len : usize ,
385+ ) -> Option < Choice > {
386+ let crate_graph = ctx. db . crate_graph ( ) ;
387+ let dependencies = & crate_graph[ ctx. from . krate ] . dependencies ;
388+ let mut best_choice = None ;
389+ let mut search = |lang, best_choice : & mut _ | {
390+ if let Some ( dep) = dependencies. iter ( ) . filter ( |it| it. is_sysroot ( ) ) . find ( |dep| {
391+ match crate_graph[ dep. crate_id ] . origin {
392+ CrateOrigin :: Lang ( l) => l == lang,
393+ _ => false ,
401394 }
402- processed_something
403- } ;
395+ } ) {
396+ find_in_dep ( ctx, visited_modules, item, max_len, best_choice, dep. crate_id ) ;
397+ }
398+ } ;
399+ if ctx. cfg . prefer_no_std {
400+ search ( LangCrateOrigin :: Core , & mut best_choice) ;
401+ if matches ! ( best_choice, Some ( Choice { stability: Stable , .. } ) ) {
402+ return best_choice;
403+ }
404+ search ( LangCrateOrigin :: Std , & mut best_choice) ;
405+ if matches ! ( best_choice, Some ( Choice { stability: Stable , .. } ) ) {
406+ return best_choice;
407+ }
408+ } else {
409+ search ( LangCrateOrigin :: Std , & mut best_choice) ;
410+ if matches ! ( best_choice, Some ( Choice { stability: Stable , .. } ) ) {
411+ return best_choice;
412+ }
413+ search ( LangCrateOrigin :: Core , & mut best_choice) ;
414+ if matches ! ( best_choice, Some ( Choice { stability: Stable , .. } ) ) {
415+ return best_choice;
416+ }
417+ }
418+ let mut best_choice = None ;
419+ dependencies. iter ( ) . filter ( |it| it. is_sysroot ( ) ) . for_each ( |dep| {
420+ find_in_dep ( ctx, visited_modules, item, max_len, & mut best_choice, dep. crate_id ) ;
421+ } ) ;
422+ best_choice
423+ }
404424
405- let crate_graph = db. crate_graph ( ) ;
406- let dependencies = & crate_graph[ ctx. from . krate ] . dependencies ;
407- if ctx. is_std_item {
408- // The item we are searching for comes from the sysroot libraries, so skip prefer looking in
409- // the sysroot libraries directly.
410- // We do need to fallback as the item in question could be re-exported by another crate
411- // while not being a transitive dependency of the current crate.
412- let processed = dependencies
413- . iter ( )
414- . filter ( |it| it. is_sysroot ( ) )
415- . map ( |dep| {
416- (
417- match crate_graph[ dep. crate_id ] . origin {
418- CrateOrigin :: Lang ( LangCrateOrigin :: Std ) if ctx. cfg . prefer_no_std => 5 ,
419- CrateOrigin :: Lang ( LangCrateOrigin :: Std ) => 1 ,
420- CrateOrigin :: Lang ( LangCrateOrigin :: Alloc ) => 2 ,
421- CrateOrigin :: Lang ( LangCrateOrigin :: ProcMacro ) => 3 ,
422- CrateOrigin :: Lang ( LangCrateOrigin :: Test ) => 3 ,
423- CrateOrigin :: Lang ( LangCrateOrigin :: Core ) => 4 ,
424- CrateOrigin :: Lang ( LangCrateOrigin :: Other ) => 1 ,
425- _ => 0 ,
426- } ,
427- dep. crate_id ,
428- )
429- } )
430- . map ( |( score, crate_id) | process_dep ( crate_id, score) )
431- . reduce ( BitOr :: bitor)
432- . unwrap_or ( false ) ;
433- if processed {
434- // Found a path in a sysroot crate
435- return ;
436- }
425+ fn find_in_dep (
426+ ctx : & FindPathCtx < ' _ > ,
427+ visited_modules : & mut FxHashSet < ( ItemInNs , ModuleId ) > ,
428+ item : ItemInNs ,
429+ max_len : usize ,
430+ best_choice : & mut Option < Choice > ,
431+ dep : CrateId ,
432+ ) {
433+ let import_map = ctx. db . import_map ( dep) ;
434+ let Some ( import_info_for) = import_map. import_info_for ( item) else {
435+ return ;
436+ } ;
437+ for info in import_info_for {
438+ if info. is_doc_hidden {
439+ // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
440+ continue ;
441+ }
442+
443+ // Determine best path for containing module and append last segment from `info`.
444+ // FIXME: we should guide this to look up the path locally, or from the same crate again?
445+ let choice = find_path_for_module (
446+ ctx,
447+ visited_modules,
448+ info. container ,
449+ true ,
450+ best_choice. as_ref ( ) . map_or ( max_len, |it| it. path . len ( ) ) - 1 ,
451+ ) ;
452+ let Some ( mut choice) = choice else {
453+ continue ;
454+ } ;
455+ cov_mark:: hit!( partially_imported) ;
456+ if info. is_unstable {
457+ choice. stability = Unstable ;
437458 }
438459
439- dependencies
440- . iter ( )
441- . filter ( |it| !ctx. is_std_item || !it. is_sysroot ( ) )
442- . for_each ( |dep| _ = process_dep ( dep. crate_id , 0 ) ) ;
460+ Choice :: try_select ( best_choice, choice, ctx. cfg . prefer_prelude , info. name . clone ( ) ) ;
443461 }
444462}
445463
@@ -481,8 +499,6 @@ struct Choice {
481499 stability : Stability ,
482500 /// Whether this path contains a prelude segment and preference for it has been signaled
483501 prefer_due_to_prelude : bool ,
484- /// non-zero if this is a path in a sysroot crate, we want to choose the highest ranked std crate
485- sysroot_score : u8 ,
486502}
487503
488504impl Choice {
@@ -491,7 +507,6 @@ impl Choice {
491507 path_text_len : path_kind_len ( kind) + name. as_str ( ) . len ( ) ,
492508 stability,
493509 prefer_due_to_prelude : prefer_prelude && name == sym:: prelude,
494- sysroot_score : 0 ,
495510 path : ModPath :: from_segments ( kind, iter:: once ( name) ) ,
496511 }
497512 }
@@ -517,7 +532,6 @@ impl Choice {
517532 . stability
518533 . cmp ( & current. stability )
519534 . then_with ( || other. prefer_due_to_prelude . cmp ( & current. prefer_due_to_prelude ) )
520- . then_with ( || current. sysroot_score . cmp ( & other. sysroot_score ) )
521535 . then_with ( || ( current. path . len ( ) ) . cmp ( & ( other. path . len ( ) + 1 ) ) )
522536 {
523537 Ordering :: Less => return ,
0 commit comments