Skip to content

Commit dd42c14

Browse files
committed
Auto merge of #16101 - Veykril:search-depedencies-fix, r=Veykril
fix: Fix `import_map::search_dependencies` getting confused by assoc and non assoc items with the same name No test case as creating one is kind of tricky... Ideally the code should be restructured such that this collision wouldn't matter in the first place, its kind of a mess. Fixes #16074 Fixes #16080 Fixes #15845
2 parents d4accf8 + ca995d7 commit dd42c14

File tree

3 files changed

+65
-35
lines changed

3 files changed

+65
-35
lines changed

crates/hir-def/src/import_map.rs

+55-32
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use indexmap::IndexMap;
99
use itertools::Itertools;
1010
use rustc_hash::{FxHashSet, FxHasher};
1111
use smallvec::SmallVec;
12+
use stdx::format_to;
1213
use triomphe::Arc;
1314

1415
use crate::{
@@ -53,13 +54,25 @@ pub struct ImportMap {
5354
fst: fst::Map<Vec<u8>>,
5455
}
5556

56-
#[derive(Copy, Clone, PartialEq, Eq)]
57+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
5758
enum IsTraitAssocItem {
5859
Yes,
5960
No,
6061
}
6162

6263
impl ImportMap {
64+
pub fn dump(&self, db: &dyn DefDatabase) -> String {
65+
let mut out = String::new();
66+
for (k, v) in self.map.iter() {
67+
format_to!(out, "{:?} ({:?}) -> ", k, v.1);
68+
for v in &v.0 {
69+
format_to!(out, "{}:{:?}, ", v.name.display(db.upcast()), v.container);
70+
}
71+
format_to!(out, "\n");
72+
}
73+
out
74+
}
75+
6376
pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
6477
let _p = profile::span("import_map_query");
6578

@@ -68,26 +81,31 @@ impl ImportMap {
6881
let mut importables: Vec<_> = map
6982
.iter()
7083
// We've only collected items, whose name cannot be tuple field.
71-
.flat_map(|(&item, (info, _))| {
72-
info.iter()
73-
.map(move |info| (item, info.name.as_str().unwrap().to_ascii_lowercase()))
84+
.flat_map(|(&item, (info, is_assoc))| {
85+
info.iter().map(move |info| {
86+
(item, *is_assoc, info.name.as_str().unwrap().to_ascii_lowercase())
87+
})
7488
})
7589
.collect();
76-
importables.sort_by(|(_, lhs_name), (_, rhs_name)| lhs_name.cmp(rhs_name));
90+
importables.sort_by(|(_, l_is_assoc, lhs_name), (_, r_is_assoc, rhs_name)| {
91+
lhs_name.cmp(rhs_name).then_with(|| l_is_assoc.cmp(r_is_assoc))
92+
});
7793
importables.dedup();
7894

7995
// Build the FST, taking care not to insert duplicate values.
8096
let mut builder = fst::MapBuilder::memory();
81-
let iter =
82-
importables.iter().enumerate().dedup_by(|(_, (_, lhs)), (_, (_, rhs))| lhs == rhs);
83-
for (start_idx, (_, name)) in iter {
97+
let iter = importables
98+
.iter()
99+
.enumerate()
100+
.dedup_by(|(_, (_, _, lhs)), (_, (_, _, rhs))| lhs == rhs);
101+
for (start_idx, (_, _, name)) in iter {
84102
let _ = builder.insert(name, start_idx as u64);
85103
}
86104

87105
Arc::new(ImportMap {
88106
map,
89107
fst: builder.into_map(),
90-
importables: importables.into_iter().map(|(item, _)| item).collect(),
108+
importables: importables.into_iter().map(|(item, _, _)| item).collect(),
91109
})
92110
}
93111

@@ -332,20 +350,20 @@ impl Query {
332350
}
333351

334352
/// Checks whether the import map entry matches the query.
335-
fn import_matches(
336-
&self,
337-
db: &dyn DefDatabase,
338-
import: &ImportInfo,
339-
enforce_lowercase: bool,
340-
) -> bool {
353+
fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
341354
let _p = profile::span("import_map::Query::import_matches");
342355

343356
// FIXME: Can we get rid of the alloc here?
344-
let mut input = import.name.display(db.upcast()).to_string();
357+
let input = import.name.to_smol_str();
358+
let mut _s_slot;
345359
let case_insensitive = enforce_lowercase || !self.case_sensitive;
346-
if case_insensitive {
347-
input.make_ascii_lowercase();
348-
}
360+
let input = if case_insensitive {
361+
_s_slot = String::from(input);
362+
_s_slot.make_ascii_lowercase();
363+
&*_s_slot
364+
} else {
365+
&*input
366+
};
349367

350368
let query_string = if case_insensitive { &self.lowercased } else { &self.query };
351369

@@ -355,7 +373,7 @@ impl Query {
355373
SearchMode::Fuzzy => {
356374
let mut input_chars = input.chars();
357375
for query_char in query_string.chars() {
358-
if input_chars.find(|&it| it == query_char).is_none() {
376+
if !input_chars.any(|it| it == query_char) {
359377
return false;
360378
}
361379
}
@@ -376,6 +394,7 @@ pub fn search_dependencies(
376394
let _p = profile::span("search_dependencies").detail(|| format!("{query:?}"));
377395

378396
let graph = db.crate_graph();
397+
379398
let import_maps: Vec<_> =
380399
graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
381400

@@ -390,22 +409,28 @@ pub fn search_dependencies(
390409

391410
let mut res = FxHashSet::default();
392411
let mut common_importable_data_scratch = vec![];
412+
// FIXME: Improve this, its rather unreadable and does duplicate amount of work
393413
while let Some((_, indexed_values)) = stream.next() {
394414
for &IndexedValue { index, value } in indexed_values {
395415
let import_map = &import_maps[index];
396-
let importables @ [importable, ..] = &import_map.importables[value as usize..] else {
416+
let importables = &import_map.importables[value as usize..];
417+
418+
// Find the first item in this group that has a matching assoc mode and slice the rest away
419+
let Some(importable) =
420+
importables.iter().position(|it| query.matches_assoc_mode(import_map.map[it].1))
421+
else {
397422
continue;
398423
};
399-
400-
let &(ref importable_data, is_trait_assoc_item) = &import_map.map[importable];
401-
if !query.matches_assoc_mode(is_trait_assoc_item) {
424+
let importables @ [importable, ..] = &importables[importable..] else {
402425
continue;
403-
}
426+
};
404427

428+
// Fetch all the known names of this importable item (to handle import aliases/renames)
405429
common_importable_data_scratch.extend(
406-
importable_data
430+
import_map.map[importable]
431+
.0
407432
.iter()
408-
.filter(|&info| query.import_matches(db, info, true))
433+
.filter(|&info| query.import_matches(info, true))
409434
// Name shared by the importable items in this group.
410435
.map(|info| info.name.to_smol_str()),
411436
);
@@ -419,6 +444,7 @@ pub fn search_dependencies(
419444
common_importable_data_scratch.drain(..).flat_map(|common_importable_name| {
420445
// Add the items from this name group. Those are all subsequent items in
421446
// `importables` whose name match `common_importable_name`.
447+
422448
importables
423449
.iter()
424450
.copied()
@@ -434,11 +460,8 @@ pub fn search_dependencies(
434460
.filter(move |item| {
435461
!query.case_sensitive || {
436462
// we've already checked the common importables name case-insensitively
437-
let &(ref import_infos, assoc_mode) = &import_map.map[item];
438-
query.matches_assoc_mode(assoc_mode)
439-
&& import_infos
440-
.iter()
441-
.any(|info| query.import_matches(db, info, false))
463+
let &(ref import_infos, _) = &import_map.map[item];
464+
import_infos.iter().any(|info| query.import_matches(info, false))
442465
}
443466
})
444467
});

crates/ide-db/src/items_locator.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ pub fn items_with_name<'a>(
3636
NameToImport::Prefix(exact_name, case_sensitive)
3737
| NameToImport::Exact(exact_name, case_sensitive) => {
3838
let mut local_query = symbol_index::Query::new(exact_name.clone());
39-
let mut external_query = import_map::Query::new(exact_name);
39+
let mut external_query =
40+
import_map::Query::new(exact_name).assoc_search_mode(assoc_item_search);
4041
if prefix {
4142
local_query.prefix();
4243
external_query = external_query.prefix();

crates/rust-analyzer/src/integrated_benchmarks.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ fn integrated_highlighting_benchmark() {
3232
let workspace_to_load = project_root();
3333
let file = "./crates/rust-analyzer/src/config.rs";
3434

35-
let cargo_config = CargoConfig::default();
35+
let cargo_config = CargoConfig {
36+
sysroot: Some(project_model::RustLibSource::Discover),
37+
..CargoConfig::default()
38+
};
3639
let load_cargo_config = LoadCargoConfig {
3740
load_out_dirs_from_check: true,
3841
with_proc_macro_server: ProcMacroServerChoice::None,
@@ -85,7 +88,10 @@ fn integrated_completion_benchmark() {
8588
let workspace_to_load = project_root();
8689
let file = "./crates/hir/src/lib.rs";
8790

88-
let cargo_config = CargoConfig::default();
91+
let cargo_config = CargoConfig {
92+
sysroot: Some(project_model::RustLibSource::Discover),
93+
..CargoConfig::default()
94+
};
8995
let load_cargo_config = LoadCargoConfig {
9096
load_out_dirs_from_check: true,
9197
with_proc_macro_server: ProcMacroServerChoice::None,

0 commit comments

Comments
 (0)