Skip to content

Commit ca995d7

Browse files
committed
fix: Fix import_map::search_dependencies getting confused by assoc and non assoc items with the same name
1 parent 3aa6306 commit ca995d7

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)