Skip to content

Commit f08aae9

Browse files
committed
Optimize find_path for sysroot library search some more
1 parent 80dd021 commit f08aae9

File tree

1 file changed

+112
-98
lines changed

1 file changed

+112
-98
lines changed

src/tools/rust-analyzer/crates/hir-def/src/find_path.rs

Lines changed: 112 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
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

55
use base_db::{CrateId, CrateOrigin, LangCrateOrigin};
66
use hir_expand::{
@@ -13,7 +13,7 @@ use rustc_hash::FxHashSet;
1313
use 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
}
6868
use Stability::*;
6969

70-
fn zip_stability(a: Stability, b: Stability) -> Stability {
71-
match (a, b) {
72-
(Stable, Stable) => Stable,
73-
_ => Unstable,
74-
}
75-
}
76-
7770
const MAX_PATH_LEN: usize = 15;
7871
const 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

488504
impl 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

Comments
 (0)