aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/src/import_map.rs287
-rw-r--r--crates/hir_def/src/item_scope.rs3
2 files changed, 177 insertions, 113 deletions
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index 30b22f51d..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,9 +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};
435 use stdx::format_to; 449 use test_utils::mark;
436 450
437 use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup}; 451 use crate::{test_db::TestDB, AssocContainerId, Lookup};
438 452
439 use super::*; 453 use super::*;
440 454
@@ -451,46 +465,66 @@ mod tests {
451 465
452 let actual = search_dependencies(db.upcast(), krate, query) 466 let actual = search_dependencies(db.upcast(), krate, query)
453 .into_iter() 467 .into_iter()
454 .filter_map(|item| { 468 .filter_map(|dependency| {
455 let mark = match item { 469 let dependency_krate = dependency.krate(db.upcast())?;
456 ItemInNs::Types(ModuleDefId::FunctionId(_)) 470 let dependency_imports = db.import_map(dependency_krate);
457 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", 471
458 ItemInNs::Types(_) => "t", 472 let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
459 ItemInNs::Values(_) => "v", 473 Some(assoc_item_path) => (assoc_item_path, "a"),
460 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 ),
461 }; 484 };
462 item.krate(db.upcast()).map(|krate| { 485
463 let map = db.import_map(krate); 486 Some(format!(
464 487 "{}::{} ({})\n",
465 let path = match assoc_to_trait(&db, item) { 488 crate_graph[dependency_krate].display_name.as_ref()?,
466 Some(trait_) => { 489 path,
467 let mut full_path = map.path_of(trait_).unwrap().to_string(); 490 mark
468 if let ItemInNs::Types(ModuleDefId::FunctionId(function_id)) 491 ))
469 | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item
470 {
471 format_to!(
472 full_path,
473 "::{}",
474 FunctionData::fn_data_query(&db, function_id).name,
475 );
476 }
477 full_path
478 }
479 None => map.path_of(item).unwrap().to_string(),
480 };
481
482 format!(
483 "{}::{} ({})\n",
484 crate_graph[krate].display_name.as_ref().unwrap(),
485 path,
486 mark
487 )
488 })
489 }) 492 })
490 .collect::<String>(); 493 .collect::<String>();
491 expect.assert_eq(&actual) 494 expect.assert_eq(&actual)
492 } 495 }
493 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
494 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> { 528 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
495 let assoc: AssocItemId = match item { 529 let assoc: AssocItemId = match item {
496 ItemInNs::Types(it) | ItemInNs::Values(it) => match it { 530 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
@@ -749,6 +783,37 @@ mod tests {
749 } 783 }
750 784
751 #[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]
752 fn search_mode() { 817 fn search_mode() {
753 let ra_fixture = r#" 818 let ra_fixture = r#"
754 //- /main.rs crate:main deps:dep 819 //- /main.rs crate:main deps:dep
@@ -784,8 +849,8 @@ mod tests {
784 dep::Fmt (v) 849 dep::Fmt (v)
785 dep::Fmt (m) 850 dep::Fmt (m)
786 dep::fmt::Display (t) 851 dep::fmt::Display (t)
852 dep::fmt::Display::fmt (a)
787 dep::format (f) 853 dep::format (f)
788 dep::fmt::Display::fmt (f)
789 "#]], 854 "#]],
790 ); 855 );
791 856
@@ -798,7 +863,7 @@ mod tests {
798 dep::Fmt (t) 863 dep::Fmt (t)
799 dep::Fmt (v) 864 dep::Fmt (v)
800 dep::Fmt (m) 865 dep::Fmt (m)
801 dep::fmt::Display::fmt (f) 866 dep::fmt::Display::fmt (a)
802 "#]], 867 "#]],
803 ); 868 );
804 869
@@ -812,7 +877,7 @@ mod tests {
812 dep::Fmt (v) 877 dep::Fmt (v)
813 dep::Fmt (m) 878 dep::Fmt (m)
814 dep::fmt::Display (t) 879 dep::fmt::Display (t)
815 dep::fmt::Display::fmt (f) 880 dep::fmt::Display::fmt (a)
816 "#]], 881 "#]],
817 ); 882 );
818 } 883 }
@@ -853,7 +918,7 @@ mod tests {
853 dep::Fmt (v) 918 dep::Fmt (v)
854 dep::Fmt (m) 919 dep::Fmt (m)
855 dep::fmt::Display (t) 920 dep::fmt::Display (t)
856 dep::fmt::Display::fmt (f) 921 dep::fmt::Display::fmt (a)
857 "#]], 922 "#]],
858 ); 923 );
859 924
@@ -866,7 +931,7 @@ mod tests {
866 dep::Fmt (t) 931 dep::Fmt (t)
867 dep::Fmt (v) 932 dep::Fmt (v)
868 dep::Fmt (m) 933 dep::Fmt (m)
869 dep::fmt::Display::fmt (f) 934 dep::fmt::Display::fmt (a)
870 "#]], 935 "#]],
871 ); 936 );
872 } 937 }
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 62ab3b2bd..2750e1c91 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -10,10 +10,9 @@ use once_cell::sync::Lazy;
10use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
11use test_utils::mark; 11use test_utils::mark;
12 12
13use crate::ModuleId;
14use crate::{ 13use crate::{
15 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, 14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
16 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, 15 LocalModuleId, Lookup, MacroDefId, ModuleDefId, ModuleId, TraitId,
17}; 16};
18 17
19#[derive(Copy, Clone)] 18#[derive(Copy, Clone)]