aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/import_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/import_map.rs')
-rw-r--r--crates/hir_def/src/import_map.rs285
1 files changed, 176 insertions, 109 deletions
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index c4dc894df..e5368b293 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -7,9 +7,8 @@ use fst::{self, Streamer};
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools; 9use itertools::Itertools;
10use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; 10use rustc_hash::{FxHashSet, FxHasher};
11use smallvec::SmallVec; 11use test_utils::mark;
12use syntax::SmolStr;
13 12
14use crate::{ 13use crate::{
15 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, 14 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
@@ -25,6 +24,8 @@ pub struct ImportInfo {
25 pub path: ImportPath, 24 pub path: ImportPath,
26 /// The module containing this item. 25 /// The module containing this item.
27 pub container: ModuleId, 26 pub container: ModuleId,
27 /// Whether the import is a trait associated item or not.
28 pub is_trait_assoc_item: bool,
28} 29}
29 30
30#[derive(Debug, Clone, Eq, PartialEq)] 31#[derive(Debug, Clone, Eq, PartialEq)]
@@ -64,10 +65,6 @@ pub struct ImportMap {
64 /// the index of the first one. 65 /// the index of the first one.
65 importables: Vec<ItemInNs>, 66 importables: Vec<ItemInNs>,
66 fst: fst::Map<Vec<u8>>, 67 fst: fst::Map<Vec<u8>>,
67
68 /// Maps names of associated items to the item's ID. Only includes items whose defining trait is
69 /// exported.
70 assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
71} 68}
72 69
73impl ImportMap { 70impl ImportMap {
@@ -108,14 +105,27 @@ impl ImportMap {
108 105
109 for item in per_ns.iter_items() { 106 for item in per_ns.iter_items() {
110 let path = mk_path(); 107 let path = mk_path();
108 let path_len = path.len();
109 let import_info =
110 ImportInfo { path, container: module, is_trait_assoc_item: false };
111
112 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
113 import_map.collect_trait_assoc_items(
114 db,
115 tr,
116 matches!(item, ItemInNs::Types(_)),
117 &import_info,
118 );
119 }
120
111 match import_map.map.entry(item) { 121 match import_map.map.entry(item) {
112 Entry::Vacant(entry) => { 122 Entry::Vacant(entry) => {
113 entry.insert(ImportInfo { path, container: module }); 123 entry.insert(import_info);
114 } 124 }
115 Entry::Occupied(mut entry) => { 125 Entry::Occupied(mut entry) => {
116 // If the new path is shorter, prefer that one. 126 // If the new path is shorter, prefer that one.
117 if path.len() < entry.get().path.len() { 127 if path_len < entry.get().path.len() {
118 *entry.get_mut() = ImportInfo { path, container: module }; 128 *entry.get_mut() = import_info;
119 } else { 129 } else {
120 continue; 130 continue;
121 } 131 }
@@ -128,11 +138,6 @@ impl ImportMap {
128 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { 138 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
129 worklist.push((mod_id, mk_path())); 139 worklist.push((mod_id, mk_path()));
130 } 140 }
131
132 // If we've added a path to a trait, add the trait's methods to the method map.
133 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
134 import_map.collect_trait_methods(db, tr);
135 }
136 } 141 }
137 } 142 }
138 } 143 }
@@ -153,12 +158,10 @@ impl ImportMap {
153 } 158 }
154 } 159 }
155 160
156 let start = last_batch_start; 161 let key = fst_path(&importables[last_batch_start].1.path);
157 last_batch_start = idx + 1; 162 builder.insert(key, last_batch_start as u64).unwrap();
158
159 let key = fst_path(&importables[start].1.path);
160 163
161 builder.insert(key, start as u64).unwrap(); 164 last_batch_start = idx + 1;
162 } 165 }
163 166
164 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); 167 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
@@ -176,10 +179,34 @@ impl ImportMap {
176 self.map.get(&item) 179 self.map.get(&item)
177 } 180 }
178 181
179 fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) { 182 fn collect_trait_assoc_items(
180 let data = db.trait_data(tr); 183 &mut self,
181 for (name, item) in data.items.iter() { 184 db: &dyn DefDatabase,
182 self.assoc_map.entry(name.to_string().into()).or_default().push(*item); 185 tr: TraitId,
186 is_type_in_ns: bool,
187 original_import_info: &ImportInfo,
188 ) {
189 for (assoc_item_name, item) in &db.trait_data(tr).items {
190 let module_def_id = match item {
191 AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
192 AssocItemId::ConstId(c) => ModuleDefId::from(*c),
193 // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
194 // qualifier, ergo no need to store it for imports in import_map
195 AssocItemId::TypeAliasId(_) => {
196 mark::hit!(type_aliases_ignored);
197 continue;
198 }
199 };
200 let assoc_item = if is_type_in_ns {
201 ItemInNs::Types(module_def_id)
202 } else {
203 ItemInNs::Values(module_def_id)
204 };
205
206 let mut assoc_item_info = original_import_info.clone();
207 assoc_item_info.path.segments.push(assoc_item_name.to_owned());
208 assoc_item_info.is_trait_assoc_item = true;
209 self.map.insert(assoc_item, assoc_item_info);
183 } 210 }
184 } 211 }
185} 212}
@@ -302,38 +329,38 @@ impl Query {
302 self.exclude_import_kinds.insert(import_kind); 329 self.exclude_import_kinds.insert(import_kind);
303 self 330 self
304 } 331 }
305}
306 332
307fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool { 333 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
308 let mut input = if query.name_only { 334 let mut input = if import.is_trait_assoc_item || self.name_only {
309 input_path.segments.last().unwrap().to_string() 335 import.path.segments.last().unwrap().to_string()
310 } else { 336 } else {
311 input_path.to_string() 337 import.path.to_string()
312 }; 338 };
313 if enforce_lowercase || !query.case_sensitive { 339 if enforce_lowercase || !self.case_sensitive {
314 input.make_ascii_lowercase(); 340 input.make_ascii_lowercase();
315 } 341 }
316 342
317 let query_string = 343 let query_string =
318 if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased }; 344 if !enforce_lowercase && self.case_sensitive { &self.query } else { &self.lowercased };
319 345
320 match query.search_mode { 346 match self.search_mode {
321 SearchMode::Equals => &input == query_string, 347 SearchMode::Equals => &input == query_string,
322 SearchMode::Contains => input.contains(query_string), 348 SearchMode::Contains => input.contains(query_string),
323 SearchMode::Fuzzy => { 349 SearchMode::Fuzzy => {
324 let mut unchecked_query_chars = query_string.chars(); 350 let mut unchecked_query_chars = query_string.chars();
325 let mut mismatching_query_char = unchecked_query_chars.next(); 351 let mut mismatching_query_char = unchecked_query_chars.next();
326 352
327 for input_char in input.chars() { 353 for input_char in input.chars() {
328 match mismatching_query_char { 354 match mismatching_query_char {
329 None => return true, 355 None => return true,
330 Some(matching_query_char) if matching_query_char == input_char => { 356 Some(matching_query_char) if matching_query_char == input_char => {
331 mismatching_query_char = unchecked_query_chars.next(); 357 mismatching_query_char = unchecked_query_chars.next();
358 }
359 _ => (),
332 } 360 }
333 _ => (),
334 } 361 }
362 mismatching_query_char.is_none()
335 } 363 }
336 mismatching_query_char.is_none()
337 } 364 }
338 } 365 }
339} 366}
@@ -366,13 +393,13 @@ pub fn search_dependencies<'a>(
366 let import_map = &import_maps[indexed_value.index]; 393 let import_map = &import_maps[indexed_value.index];
367 let importables = &import_map.importables[indexed_value.value as usize..]; 394 let importables = &import_map.importables[indexed_value.value as usize..];
368 395
369 // Path shared by the importable items in this group. 396 let common_importable_data = &import_map.map[&importables[0]];
370 let common_importables_path = &import_map.map[&importables[0]].path; 397 if !query.import_matches(common_importable_data, true) {
371 if !contains_query(&query, common_importables_path, true) {
372 continue; 398 continue;
373 } 399 }
374 400
375 let common_importables_path_fst = fst_path(common_importables_path); 401 // Path shared by the importable items in this group.
402 let common_importables_path_fst = fst_path(&common_importable_data.path);
376 // Add the items from this `ModPath` group. Those are all subsequent items in 403 // Add the items from this `ModPath` group. Those are all subsequent items in
377 // `importables` whose paths match `path`. 404 // `importables` whose paths match `path`.
378 let iter = importables 405 let iter = importables
@@ -387,7 +414,7 @@ pub fn search_dependencies<'a>(
387 }) 414 })
388 .filter(|item| { 415 .filter(|item| {
389 !query.case_sensitive // we've already checked the common importables path case-insensitively 416 !query.case_sensitive // we've already checked the common importables path case-insensitively
390 || contains_query(&query, &import_map.map[item].path, false) 417 || query.import_matches(&import_map.map[item], false)
391 }); 418 });
392 res.extend(iter); 419 res.extend(iter);
393 420
@@ -398,19 +425,6 @@ pub fn search_dependencies<'a>(
398 } 425 }
399 } 426 }
400 427
401 // Add all exported associated items whose names match the query (exactly).
402 for map in &import_maps {
403 if let Some(v) = map.assoc_map.get(&*query.query) {
404 res.extend(v.iter().map(|&assoc| {
405 ItemInNs::Types(match assoc {
406 AssocItemId::FunctionId(it) => it.into(),
407 AssocItemId::ConstId(it) => it.into(),
408 AssocItemId::TypeAliasId(it) => it.into(),
409 })
410 }));
411 }
412 }
413
414 res 428 res
415} 429}
416 430
@@ -432,8 +446,9 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
432mod tests { 446mod tests {
433 use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; 447 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
434 use expect_test::{expect, Expect}; 448 use expect_test::{expect, Expect};
449 use test_utils::mark;
435 450
436 use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup}; 451 use crate::{test_db::TestDB, AssocContainerId, Lookup};
437 452
438 use super::*; 453 use super::*;
439 454
@@ -450,45 +465,66 @@ mod tests {
450 465
451 let actual = search_dependencies(db.upcast(), krate, query) 466 let actual = search_dependencies(db.upcast(), krate, query)
452 .into_iter() 467 .into_iter()
453 .filter_map(|item| { 468 .filter_map(|dependency| {
454 let mark = match item { 469 let dependency_krate = dependency.krate(db.upcast())?;
455 ItemInNs::Types(ModuleDefId::FunctionId(_)) 470 let dependency_imports = db.import_map(dependency_krate);
456 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", 471
457 ItemInNs::Types(_) => "t", 472 let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
458 ItemInNs::Values(_) => "v", 473 Some(assoc_item_path) => (assoc_item_path, "a"),
459 ItemInNs::Macros(_) => "m", 474 None => (
475 dependency_imports.path_of(dependency)?.to_string(),
476 match dependency {
477 ItemInNs::Types(ModuleDefId::FunctionId(_))
478 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
479 ItemInNs::Types(_) => "t",
480 ItemInNs::Values(_) => "v",
481 ItemInNs::Macros(_) => "m",
482 },
483 ),
460 }; 484 };
461 item.krate(db.upcast()).map(|krate| { 485
462 let map = db.import_map(krate); 486 Some(format!(
463 487 "{}::{} ({})\n",
464 let path = match assoc_to_trait(&db, item) { 488 crate_graph[dependency_krate].display_name.as_ref()?,
465 Some(trait_) => { 489 path,
466 let mut full_path = map.path_of(trait_).unwrap().to_string(); 490 mark
467 if let ItemInNs::Types(ModuleDefId::FunctionId(function_id)) 491 ))
468 | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item
469 {
470 full_path += &format!(
471 "::{}",
472 FunctionData::fn_data_query(&db, function_id).name
473 );
474 }
475 full_path
476 }
477 None => map.path_of(item).unwrap().to_string(),
478 };
479
480 format!(
481 "{}::{} ({})\n",
482 crate_graph[krate].display_name.as_ref().unwrap(),
483 path,
484 mark
485 )
486 })
487 }) 492 })
488 .collect::<String>(); 493 .collect::<String>();
489 expect.assert_eq(&actual) 494 expect.assert_eq(&actual)
490 } 495 }
491 496
497 fn assoc_item_path(
498 db: &dyn DefDatabase,
499 dependency_imports: &ImportMap,
500 dependency: ItemInNs,
501 ) -> Option<String> {
502 let dependency_assoc_item_id = match dependency {
503 ItemInNs::Types(ModuleDefId::FunctionId(id))
504 | ItemInNs::Values(ModuleDefId::FunctionId(id)) => AssocItemId::from(id),
505 ItemInNs::Types(ModuleDefId::ConstId(id))
506 | ItemInNs::Values(ModuleDefId::ConstId(id)) => AssocItemId::from(id),
507 ItemInNs::Types(ModuleDefId::TypeAliasId(id))
508 | ItemInNs::Values(ModuleDefId::TypeAliasId(id)) => AssocItemId::from(id),
509 _ => return None,
510 };
511
512 let trait_ = assoc_to_trait(db, dependency)?;
513 if let ModuleDefId::TraitId(tr) = trait_.as_module_def_id()? {
514 let trait_data = db.trait_data(tr);
515 let assoc_item_name =
516 trait_data.items.iter().find_map(|(assoc_item_name, assoc_item_id)| {
517 if &dependency_assoc_item_id == assoc_item_id {
518 Some(assoc_item_name)
519 } else {
520 None
521 }
522 })?;
523 return Some(format!("{}::{}", dependency_imports.path_of(trait_)?, assoc_item_name));
524 }
525 None
526 }
527
492 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> { 528 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
493 let assoc: AssocItemId = match item { 529 let assoc: AssocItemId = match item {
494 ItemInNs::Types(it) | ItemInNs::Values(it) => match it { 530 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
@@ -747,6 +783,37 @@ mod tests {
747 } 783 }
748 784
749 #[test] 785 #[test]
786 fn fuzzy_import_trait_and_assoc_items() {
787 mark::check!(type_aliases_ignored);
788 let ra_fixture = r#"
789 //- /main.rs crate:main deps:dep
790 //- /dep.rs crate:dep
791 pub mod fmt {
792 pub trait Display {
793 type FmtTypeAlias;
794 const FMT_CONST: bool;
795
796 fn format_function();
797 fn format_method(&self);
798 }
799 }
800 "#;
801
802 check_search(
803 ra_fixture,
804 "main",
805 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
806 expect![[r#"
807 dep::fmt (t)
808 dep::fmt::Display (t)
809 dep::fmt::Display::FMT_CONST (a)
810 dep::fmt::Display::format_function (a)
811 dep::fmt::Display::format_method (a)
812 "#]],
813 );
814 }
815
816 #[test]
750 fn search_mode() { 817 fn search_mode() {
751 let ra_fixture = r#" 818 let ra_fixture = r#"
752 //- /main.rs crate:main deps:dep 819 //- /main.rs crate:main deps:dep
@@ -782,8 +849,8 @@ mod tests {
782 dep::Fmt (v) 849 dep::Fmt (v)
783 dep::Fmt (m) 850 dep::Fmt (m)
784 dep::fmt::Display (t) 851 dep::fmt::Display (t)
852 dep::fmt::Display::fmt (a)
785 dep::format (f) 853 dep::format (f)
786 dep::fmt::Display::fmt (f)
787 "#]], 854 "#]],
788 ); 855 );
789 856
@@ -796,7 +863,7 @@ mod tests {
796 dep::Fmt (t) 863 dep::Fmt (t)
797 dep::Fmt (v) 864 dep::Fmt (v)
798 dep::Fmt (m) 865 dep::Fmt (m)
799 dep::fmt::Display::fmt (f) 866 dep::fmt::Display::fmt (a)
800 "#]], 867 "#]],
801 ); 868 );
802 869
@@ -810,7 +877,7 @@ mod tests {
810 dep::Fmt (v) 877 dep::Fmt (v)
811 dep::Fmt (m) 878 dep::Fmt (m)
812 dep::fmt::Display (t) 879 dep::fmt::Display (t)
813 dep::fmt::Display::fmt (f) 880 dep::fmt::Display::fmt (a)
814 "#]], 881 "#]],
815 ); 882 );
816 } 883 }
@@ -851,7 +918,7 @@ mod tests {
851 dep::Fmt (v) 918 dep::Fmt (v)
852 dep::Fmt (m) 919 dep::Fmt (m)
853 dep::fmt::Display (t) 920 dep::fmt::Display (t)
854 dep::fmt::Display::fmt (f) 921 dep::fmt::Display::fmt (a)
855 "#]], 922 "#]],
856 ); 923 );
857 924
@@ -864,7 +931,7 @@ mod tests {
864 dep::Fmt (t) 931 dep::Fmt (t)
865 dep::Fmt (v) 932 dep::Fmt (v)
866 dep::Fmt (m) 933 dep::Fmt (m)
867 dep::fmt::Display::fmt (f) 934 dep::fmt::Display::fmt (a)
868 "#]], 935 "#]],
869 ); 936 );
870 } 937 }