1
1
//! An algorithm to find a path to refer to a certain item.
2
2
3
- use std:: { cell:: Cell , cmp:: Ordering , iter, ops :: BitOr } ;
3
+ use std:: { cell:: Cell , cmp:: Ordering , iter} ;
4
4
5
5
use base_db:: { CrateId , CrateOrigin , LangCrateOrigin } ;
6
6
use hir_expand:: {
@@ -13,7 +13,7 @@ use rustc_hash::FxHashSet;
13
13
use crate :: {
14
14
db:: DefDatabase ,
15
15
item_scope:: ItemInNs ,
16
- nameres:: { DefMap , ModuleData } ,
16
+ nameres:: DefMap ,
17
17
path:: { ModPath , PathKind } ,
18
18
visibility:: { Visibility , VisibilityExplicitness } ,
19
19
ImportPathConfig , ModuleDefId , ModuleId ,
@@ -52,11 +52,11 @@ pub fn find_path(
52
52
ignore_local_imports,
53
53
from,
54
54
from_def_map : & from. def_map ( db) ,
55
- is_std_item : db. crate_graph ( ) [ item_module. krate ( ) ] . origin . is_lang ( ) ,
56
55
fuel : Cell :: new ( FIND_PATH_FUEL ) ,
57
56
} ,
58
57
item,
59
58
MAX_PATH_LEN ,
59
+ db. crate_graph ( ) [ item_module. krate ( ) ] . origin . is_lang ( ) ,
60
60
)
61
61
}
62
62
@@ -67,13 +67,6 @@ enum Stability {
67
67
}
68
68
use Stability :: * ;
69
69
70
- fn zip_stability ( a : Stability , b : Stability ) -> Stability {
71
- match ( a, b) {
72
- ( Stable , Stable ) => Stable ,
73
- _ => Unstable ,
74
- }
75
- }
76
-
77
70
const MAX_PATH_LEN : usize = 15 ;
78
71
const FIND_PATH_FUEL : usize = 10000 ;
79
72
@@ -107,17 +100,22 @@ struct FindPathCtx<'db> {
107
100
ignore_local_imports : bool ,
108
101
from : ModuleId ,
109
102
from_def_map : & ' db DefMap ,
110
- is_std_item : bool ,
111
103
fuel : Cell < usize > ,
112
104
}
113
105
114
106
/// 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 > {
116
113
// - 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
+ }
121
119
}
122
120
123
121
let may_be_in_scope = match ctx. prefix {
@@ -140,9 +138,12 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
140
138
141
139
if let Some ( ModuleDefId :: EnumVariantId ( variant) ) = item. as_module_def_id ( ) {
142
140
// - 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
+ ) {
146
147
path. push_segment ( ctx. db . enum_variant_data ( variant) . name . clone ( ) ) ;
147
148
return Some ( path) ;
148
149
}
@@ -151,10 +152,18 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
151
152
// variant somewhere
152
153
}
153
154
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
+ }
155
164
156
165
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) ;
158
167
best_choice. map ( |choice| choice. path )
159
168
}
160
169
@@ -178,7 +187,6 @@ fn find_path_for_module(
178
187
path_text_len : 5 ,
179
188
stability : Stable ,
180
189
prefer_due_to_prelude : false ,
181
- sysroot_score : 0 ,
182
190
} ) ;
183
191
}
184
192
// - 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(
241
249
path_text_len : path_kind_len ( kind) ,
242
250
stability : Stable ,
243
251
prefer_due_to_prelude : false ,
244
- sysroot_score : 0 ,
245
252
} ) ;
246
253
}
247
254
}
@@ -360,86 +367,97 @@ fn calculate_best_path(
360
367
// dependency in this case.
361
368
calculate_best_path_local ( ctx, visited_modules, item, max_len, best_choice)
362
369
} else {
363
- let db = ctx. db ;
364
370
// Item was defined in some upstream crate. This means that it must be exported from one,
365
371
// too (unless we can't name it at all). It could *also* be (re)exported by the same crate
366
372
// that wants to import it here, but we always prefer to use the external path here.
367
373
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
+ }
379
379
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 ,
401
394
}
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
+ }
404
424
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 ;
437
458
}
438
459
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 ( ) ) ;
443
461
}
444
462
}
445
463
@@ -481,8 +499,6 @@ struct Choice {
481
499
stability : Stability ,
482
500
/// Whether this path contains a prelude segment and preference for it has been signaled
483
501
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 ,
486
502
}
487
503
488
504
impl Choice {
@@ -491,7 +507,6 @@ impl Choice {
491
507
path_text_len : path_kind_len ( kind) + name. as_str ( ) . len ( ) ,
492
508
stability,
493
509
prefer_due_to_prelude : prefer_prelude && name == sym:: prelude,
494
- sysroot_score : 0 ,
495
510
path : ModPath :: from_segments ( kind, iter:: once ( name) ) ,
496
511
}
497
512
}
@@ -517,7 +532,6 @@ impl Choice {
517
532
. stability
518
533
. cmp ( & current. stability )
519
534
. then_with ( || other. prefer_due_to_prelude . cmp ( & current. prefer_due_to_prelude ) )
520
- . then_with ( || current. sysroot_score . cmp ( & other. sysroot_score ) )
521
535
. then_with ( || ( current. path . len ( ) ) . cmp ( & ( other. path . len ( ) + 1 ) ) )
522
536
{
523
537
Ordering :: Less => return ,
0 commit comments