1use std::fmt;
4
5use base_db::Crate;
6use fst::{Automaton, Streamer, raw::IndexedValue};
7use hir_expand::name::Name;
8use itertools::Itertools;
9use rustc_hash::FxHashSet;
10use smallvec::SmallVec;
11use span::Edition;
12use stdx::format_to;
13use triomphe::Arc;
14
15use crate::{
16 AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId,
17 db::DefDatabase,
18 item_scope::{ImportOrExternCrate, ItemInNs},
19 nameres::{DefMap, assoc::TraitItems, crate_def_map},
20 visibility::Visibility,
21};
22
23#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
25pub struct ImportInfo {
26 pub name: Name,
28 pub container: ModuleId,
30 pub is_doc_hidden: bool,
32 pub is_unstable: bool,
34 pub complete: Complete,
36}
37
38#[derive(Default)]
42pub struct ImportMap {
43 item_to_info_map: ImportMapIndex,
45 importables: Vec<(ItemInNs, u32)>,
54 fst: fst::Map<Vec<u8>>,
55}
56
57#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
58enum IsTraitAssocItem {
59 Yes,
60 No,
61}
62
63type ImportMapIndex = FxIndexMap<ItemInNs, (SmallVec<[ImportInfo; 1]>, IsTraitAssocItem)>;
64
65impl ImportMap {
66 pub fn dump(&self, db: &dyn DefDatabase) -> String {
67 let mut out = String::new();
68 for (k, v) in self.item_to_info_map.iter() {
69 format_to!(out, "{:?} ({:?}) -> ", k, v.1);
70 for v in &v.0 {
71 format_to!(out, "{}:{:?}, ", v.name.display(db, Edition::CURRENT), v.container);
72 }
73 format_to!(out, "\n");
74 }
75 out
76 }
77
78 pub(crate) fn import_map_query(db: &dyn DefDatabase, krate: Crate) -> Arc<Self> {
79 let _p = tracing::info_span!("import_map_query").entered();
80
81 let map = Self::collect_import_map(db, krate);
82
83 let mut importables: Vec<_> = map
84 .iter()
85 .flat_map(|(&item, (info, _))| {
87 info.iter()
88 .enumerate()
89 .map(move |(idx, info)| (item, info.name.as_str(), idx as u32))
90 })
91 .collect();
92 importables.sort_by(|(_, l_info, _), (_, r_info, _)| {
93 let lhs_chars = l_info.chars().map(|c| c.to_ascii_lowercase());
94 let rhs_chars = r_info.chars().map(|c| c.to_ascii_lowercase());
95 lhs_chars.cmp(rhs_chars)
96 });
97 importables.dedup();
98
99 let mut builder = fst::MapBuilder::memory();
101 let mut iter = importables
102 .iter()
103 .enumerate()
104 .dedup_by(|&(_, (_, lhs, _)), &(_, (_, rhs, _))| lhs.eq_ignore_ascii_case(rhs));
105
106 let mut insert = |name: &str, start, end| {
107 builder.insert(name.to_ascii_lowercase(), ((start as u64) << 32) | end as u64).unwrap()
108 };
109
110 if let Some((mut last, (_, name, _))) = iter.next() {
111 debug_assert_eq!(last, 0);
112 let mut last_name = name;
113 for (next, (_, next_name, _)) in iter {
114 insert(last_name, last, next);
115 last = next;
116 last_name = next_name;
117 }
118 insert(last_name, last, importables.len());
119 }
120
121 let importables = importables.into_iter().map(|(item, _, idx)| (item, idx)).collect();
122 Arc::new(ImportMap { item_to_info_map: map, fst: builder.into_map(), importables })
123 }
124
125 pub fn import_info_for(&self, item: ItemInNs) -> Option<&[ImportInfo]> {
126 self.item_to_info_map.get(&item).map(|(info, _)| &**info)
127 }
128
129 fn collect_import_map(db: &dyn DefDatabase, krate: Crate) -> ImportMapIndex {
130 let _p = tracing::info_span!("collect_import_map").entered();
131
132 let def_map = crate_def_map(db, krate);
133 let mut map = FxIndexMap::default();
134
135 let root = def_map.module_id(DefMap::ROOT);
137 let mut worklist = vec![root];
138 let mut visited = FxHashSet::default();
139
140 while let Some(module) = worklist.pop() {
141 if !visited.insert(module) {
142 continue;
143 }
144 let ext_def_map;
145 let mod_data = if module.krate == krate {
146 &def_map[module.local_id]
147 } else {
148 ext_def_map = module.def_map(db);
150 &ext_def_map[module.local_id]
151 };
152
153 let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
154 let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
155 if per_ns.is_none() { None } else { Some((name, per_ns)) }
156 });
157
158 for (name, per_ns) in visible_items {
159 for (item, import) in per_ns.iter_items() {
160 let attr_id = if let Some(import) = import {
161 match import {
162 ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
163 ImportOrExternCrate::Import(id) => Some(id.use_.into()),
164 ImportOrExternCrate::Glob(id) => Some(id.use_.into()),
165 }
166 } else {
167 match item {
168 ItemInNs::Types(id) | ItemInNs::Values(id) => id.try_into().ok(),
169 ItemInNs::Macros(id) => Some(id.into()),
170 }
171 };
172 let (is_doc_hidden, is_unstable, do_not_complete) = match attr_id {
173 None => (false, false, Complete::Yes),
174 Some(attr_id) => {
175 let attrs = db.attrs(attr_id);
176 let do_not_complete =
177 Complete::extract(matches!(attr_id, AttrDefId::TraitId(_)), &attrs);
178 (attrs.has_doc_hidden(), attrs.is_unstable(), do_not_complete)
179 }
180 };
181
182 let import_info = ImportInfo {
183 name: name.clone(),
184 container: module,
185 is_doc_hidden,
186 is_unstable,
187 complete: do_not_complete,
188 };
189
190 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
191 Self::collect_trait_assoc_items(
192 db,
193 &mut map,
194 tr,
195 matches!(item, ItemInNs::Types(_)),
196 &import_info,
197 );
198 }
199
200 let (infos, _) =
201 map.entry(item).or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::No));
202 infos.reserve_exact(1);
203 infos.push(import_info);
204
205 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
207 worklist.push(mod_id);
208 }
209 }
210 }
211 }
212 map.shrink_to_fit();
213 map
214 }
215
216 fn collect_trait_assoc_items(
217 db: &dyn DefDatabase,
218 map: &mut ImportMapIndex,
219 tr: TraitId,
220 is_type_in_ns: bool,
221 trait_import_info: &ImportInfo,
222 ) {
223 let _p = tracing::info_span!("collect_trait_assoc_items").entered();
224 for &(ref assoc_item_name, item) in &TraitItems::query(db, tr).items {
225 let module_def_id = match item {
226 AssocItemId::FunctionId(f) => ModuleDefId::from(f),
227 AssocItemId::ConstId(c) => ModuleDefId::from(c),
228 AssocItemId::TypeAliasId(_) => {
231 cov_mark::hit!(type_aliases_ignored);
232 continue;
233 }
234 };
235 let assoc_item = if is_type_in_ns {
236 ItemInNs::Types(module_def_id)
237 } else {
238 ItemInNs::Values(module_def_id)
239 };
240
241 let attr_id = item.into();
242 let attrs = &db.attrs(attr_id);
243 let item_do_not_complete = Complete::extract(false, attrs);
244 let do_not_complete =
245 Complete::for_trait_item(trait_import_info.complete, item_do_not_complete);
246 let assoc_item_info = ImportInfo {
247 container: trait_import_info.container,
248 name: assoc_item_name.clone(),
249 is_doc_hidden: attrs.has_doc_hidden(),
250 is_unstable: attrs.is_unstable(),
251 complete: do_not_complete,
252 };
253
254 let (infos, _) =
255 map.entry(assoc_item).or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::Yes));
256 infos.reserve_exact(1);
257 infos.push(assoc_item_info);
258 }
259 }
260}
261
262impl Eq for ImportMap {}
263impl PartialEq for ImportMap {
264 fn eq(&self, other: &Self) -> bool {
265 self.item_to_info_map == other.item_to_info_map
267 }
268}
269
270impl fmt::Debug for ImportMap {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 let mut importable_names: Vec<_> = self
273 .item_to_info_map
274 .iter()
275 .map(|(item, (infos, _))| {
276 let l = infos.len();
277 match item {
278 ItemInNs::Types(it) => format!("- {it:?} (t) [{l}]",),
279 ItemInNs::Values(it) => format!("- {it:?} (v) [{l}]",),
280 ItemInNs::Macros(it) => format!("- {it:?} (m) [{l}]",),
281 }
282 })
283 .collect();
284
285 importable_names.sort();
286 f.write_str(&importable_names.join("\n"))
287 }
288}
289
290#[derive(Debug, Copy, Clone, PartialEq, Eq)]
292pub enum SearchMode {
293 Exact,
295 Fuzzy,
298 Prefix,
300}
301
302impl SearchMode {
303 pub fn check(self, query: &str, case_sensitive: bool, candidate: &str) -> bool {
304 match self {
305 SearchMode::Exact if case_sensitive => candidate == query,
306 SearchMode::Exact => candidate.eq_ignore_ascii_case(query),
307 SearchMode::Prefix => {
308 query.len() <= candidate.len() && {
309 let prefix = &candidate[..query.len()];
310 if case_sensitive {
311 prefix == query
312 } else {
313 prefix.eq_ignore_ascii_case(query)
314 }
315 }
316 }
317 SearchMode::Fuzzy => {
318 let mut name = candidate;
319 query.chars().all(|query_char| {
320 let m = if case_sensitive {
321 name.match_indices(query_char).next()
322 } else {
323 name.match_indices([query_char, query_char.to_ascii_uppercase()]).next()
324 };
325 match m {
326 Some((index, _)) => {
327 name = name[index..].strip_prefix(|_: char| true).unwrap_or_default();
328 true
329 }
330 None => false,
331 }
332 })
333 }
334 }
335 }
336}
337
338#[derive(Debug, Clone, Copy)]
340pub enum AssocSearchMode {
341 Include,
343 Exclude,
345 AssocItemsOnly,
347}
348
349#[derive(Debug)]
350pub struct Query {
351 query: String,
352 lowercased: String,
353 search_mode: SearchMode,
354 assoc_mode: AssocSearchMode,
355 case_sensitive: bool,
356}
357
358impl Query {
359 pub fn new(query: String) -> Self {
360 let lowercased = query.to_lowercase();
361 Self {
362 query,
363 lowercased,
364 search_mode: SearchMode::Exact,
365 assoc_mode: AssocSearchMode::Include,
366 case_sensitive: false,
367 }
368 }
369
370 pub fn fuzzy(self) -> Self {
372 Self { search_mode: SearchMode::Fuzzy, ..self }
373 }
374
375 pub fn prefix(self) -> Self {
376 Self { search_mode: SearchMode::Prefix, ..self }
377 }
378
379 pub fn exact(self) -> Self {
380 Self { search_mode: SearchMode::Exact, ..self }
381 }
382
383 pub fn assoc_search_mode(self, assoc_mode: AssocSearchMode) -> Self {
385 Self { assoc_mode, ..self }
386 }
387
388 pub fn case_sensitive(self) -> Self {
390 Self { case_sensitive: true, ..self }
391 }
392
393 fn matches_assoc_mode(&self, is_trait_assoc_item: IsTraitAssocItem) -> bool {
394 !matches!(
395 (is_trait_assoc_item, self.assoc_mode),
396 (IsTraitAssocItem::Yes, AssocSearchMode::Exclude)
397 | (IsTraitAssocItem::No, AssocSearchMode::AssocItemsOnly)
398 )
399 }
400}
401
402pub fn search_dependencies(
406 db: &dyn DefDatabase,
407 krate: Crate,
408 query: &Query,
409) -> FxHashSet<(ItemInNs, Complete)> {
410 let _p = tracing::info_span!("search_dependencies", ?query).entered();
411
412 let import_maps: Vec<_> =
413 krate.data(db).dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
414
415 let mut op = fst::map::OpBuilder::new();
416
417 match query.search_mode {
418 SearchMode::Exact => {
419 let automaton = fst::automaton::Str::new(&query.lowercased);
420
421 for map in &import_maps {
422 op = op.add(map.fst.search(&automaton));
423 }
424 search_maps(db, &import_maps, op.union(), query)
425 }
426 SearchMode::Fuzzy => {
427 let automaton = fst::automaton::Subsequence::new(&query.lowercased);
428
429 for map in &import_maps {
430 op = op.add(map.fst.search(&automaton));
431 }
432 search_maps(db, &import_maps, op.union(), query)
433 }
434 SearchMode::Prefix => {
435 let automaton = fst::automaton::Str::new(&query.lowercased).starts_with();
436
437 for map in &import_maps {
438 op = op.add(map.fst.search(&automaton));
439 }
440 search_maps(db, &import_maps, op.union(), query)
441 }
442 }
443}
444
445fn search_maps(
446 _db: &dyn DefDatabase,
447 import_maps: &[Arc<ImportMap>],
448 mut stream: fst::map::Union<'_>,
449 query: &Query,
450) -> FxHashSet<(ItemInNs, Complete)> {
451 let mut res = FxHashSet::default();
452 while let Some((_, indexed_values)) = stream.next() {
453 for &IndexedValue { index: import_map_idx, value } in indexed_values {
454 let end = (value & 0xFFFF_FFFF) as usize;
455 let start = (value >> 32) as usize;
456 let ImportMap { item_to_info_map, importables, .. } = &*import_maps[import_map_idx];
457 let importables = &importables[start..end];
458
459 let iter = importables
460 .iter()
461 .copied()
462 .filter_map(|(item, info_idx)| {
463 let (import_infos, assoc_mode) = &item_to_info_map[&item];
464 query
465 .matches_assoc_mode(*assoc_mode)
466 .then(|| (item, &import_infos[info_idx as usize]))
467 })
468 .filter(|&(_, info)| {
469 query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str())
470 })
471 .map(|(item, import_info)| (item, import_info.complete));
472 res.extend(iter);
473 }
474 }
475
476 res
477}
478
479#[cfg(test)]
480mod tests {
481 use base_db::RootQueryDb;
482 use expect_test::{Expect, expect};
483 use test_fixture::WithFixture;
484
485 use crate::{ItemContainerId, Lookup, nameres::assoc::TraitItems, test_db::TestDB};
486
487 use super::*;
488
489 impl ImportMap {
490 fn fmt_for_test(&self, db: &dyn DefDatabase) -> String {
491 let mut importable_paths: Vec<_> = self
492 .item_to_info_map
493 .iter()
494 .flat_map(|(item, (info, _))| info.iter().map(move |info| (item, info)))
495 .map(|(item, info)| {
496 let path = render_path(db, info);
497 let ns = match item {
498 ItemInNs::Types(_) => "t",
499 ItemInNs::Values(_) => "v",
500 ItemInNs::Macros(_) => "m",
501 };
502 format!("- {path} ({ns})")
503 })
504 .collect();
505
506 importable_paths.sort();
507 importable_paths.join("\n")
508 }
509 }
510
511 fn check_search(
512 #[rust_analyzer::rust_fixture] ra_fixture: &str,
513 crate_name: &str,
514 query: Query,
515 expect: Expect,
516 ) {
517 let db = TestDB::with_files(ra_fixture);
518 let all_crates = db.all_crates();
519 let krate = all_crates
520 .iter()
521 .copied()
522 .find(|&krate| {
523 krate
524 .extra_data(&db)
525 .display_name
526 .as_ref()
527 .is_some_and(|it| it.crate_name().as_str() == crate_name)
528 })
529 .expect("could not find crate");
530
531 let actual = search_dependencies(&db, krate, &query)
532 .into_iter()
533 .filter_map(|(dependency, _)| {
534 let dependency_krate = dependency.krate(&db)?;
535 let dependency_imports = db.import_map(dependency_krate);
536
537 let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
538 Some(assoc_item_path) => (assoc_item_path, "a"),
539 None => (
540 render_path(&db, &dependency_imports.import_info_for(dependency)?[0]),
541 match dependency {
542 ItemInNs::Types(ModuleDefId::FunctionId(_))
543 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
544 ItemInNs::Types(_) => "t",
545 ItemInNs::Values(_) => "v",
546 ItemInNs::Macros(_) => "m",
547 },
548 ),
549 };
550
551 Some(format!(
552 "{}::{} ({})\n",
553 dependency_krate.extra_data(&db).display_name.as_ref()?,
554 path,
555 mark
556 ))
557 })
558 .sorted()
561 .collect::<String>();
562 expect.assert_eq(&actual)
563 }
564
565 fn assoc_item_path(
566 db: &dyn DefDatabase,
567 dependency_imports: &ImportMap,
568 dependency: ItemInNs,
569 ) -> Option<String> {
570 let (dependency_assoc_item_id, container) = match dependency.as_module_def_id()? {
571 ModuleDefId::FunctionId(id) => (AssocItemId::from(id), id.lookup(db).container),
572 ModuleDefId::ConstId(id) => (AssocItemId::from(id), id.lookup(db).container),
573 ModuleDefId::TypeAliasId(id) => (AssocItemId::from(id), id.lookup(db).container),
574 _ => return None,
575 };
576
577 let ItemContainerId::TraitId(trait_id) = container else {
578 return None;
579 };
580
581 let trait_info = dependency_imports.import_info_for(ItemInNs::Types(trait_id.into()))?;
582
583 let trait_items = TraitItems::query(db, trait_id);
584 let (assoc_item_name, _) = trait_items
585 .items
586 .iter()
587 .find(|(_, assoc_item_id)| &dependency_assoc_item_id == assoc_item_id)?;
588 Some(format!(
590 "{}::{}",
591 render_path(db, &trait_info[0]),
592 assoc_item_name.display(db, Edition::CURRENT)
593 ))
594 }
595
596 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
597 let db = TestDB::with_files(ra_fixture);
598 let all_crates = db.all_crates();
599
600 let actual = all_crates
601 .iter()
602 .copied()
603 .filter_map(|krate| {
604 let cdata = &krate.extra_data(&db);
605 let name = cdata.display_name.as_ref()?;
606
607 let map = db.import_map(krate);
608
609 Some(format!("{name}:\n{}\n", map.fmt_for_test(&db)))
610 })
611 .sorted()
612 .collect::<String>();
613
614 expect.assert_eq(&actual)
615 }
616
617 fn render_path(db: &dyn DefDatabase, info: &ImportInfo) -> String {
618 let mut module = info.container;
619 let mut segments = vec![&info.name];
620
621 let def_map = module.def_map(db);
622 assert!(def_map.block_id().is_none(), "block local items should not be in `ImportMap`");
623
624 while let Some(parent) = module.containing_module(db) {
625 let parent_data = &def_map[parent.local_id];
626 let (name, _) =
627 parent_data.children.iter().find(|(_, id)| **id == module.local_id).unwrap();
628 segments.push(name);
629 module = parent;
630 }
631
632 segments.iter().rev().map(|it| it.display(db, Edition::CURRENT)).join("::")
633 }
634
635 #[test]
636 fn smoke() {
637 check(
638 r"
639 //- /main.rs crate:main deps:lib
640
641 mod private {
642 pub use lib::Pub;
643 pub struct InPrivateModule;
644 }
645
646 pub mod publ1 {
647 use lib::Pub;
648 }
649
650 pub mod real_pub {
651 pub use lib::Pub;
652 }
653 pub mod real_pu2 { // same path length as above
654 pub use lib::Pub;
655 }
656
657 //- /lib.rs crate:lib
658 pub struct Pub {}
659 pub struct Pub2; // t + v
660 struct Priv;
661 ",
662 expect![[r#"
663 lib:
664 - Pub (t)
665 - Pub2 (t)
666 - Pub2 (v)
667 main:
668 - publ1 (t)
669 - real_pu2 (t)
670 - real_pu2::Pub (t)
671 - real_pub (t)
672 - real_pub::Pub (t)
673 "#]],
674 );
675 }
676
677 #[test]
678 fn prefers_shortest_path() {
679 check(
680 r"
681 //- /main.rs crate:main
682
683 pub mod sub {
684 pub mod subsub {
685 pub struct Def {}
686 }
687
688 pub use super::sub::subsub::Def;
689 }
690 ",
691 expect![[r#"
692 main:
693 - sub (t)
694 - sub::Def (t)
695 - sub::subsub (t)
696 - sub::subsub::Def (t)
697 "#]],
698 );
699 }
700
701 #[test]
702 fn type_reexport_cross_crate() {
703 check(
706 r"
707 //- /main.rs crate:main deps:lib
708 pub mod m {
709 pub use lib::S;
710 }
711 //- /lib.rs crate:lib
712 pub struct S;
713 ",
714 expect![[r#"
715 lib:
716 - S (t)
717 - S (v)
718 main:
719 - m (t)
720 - m::S (t)
721 - m::S (v)
722 "#]],
723 );
724 }
725
726 #[test]
727 fn macro_reexport() {
728 check(
729 r"
730 //- /main.rs crate:main deps:lib
731 pub mod m {
732 pub use lib::pub_macro;
733 }
734 //- /lib.rs crate:lib
735 #[macro_export]
736 macro_rules! pub_macro {
737 () => {};
738 }
739 ",
740 expect![[r#"
741 lib:
742 - pub_macro (m)
743 main:
744 - m (t)
745 - m::pub_macro (m)
746 "#]],
747 );
748 }
749
750 #[test]
751 fn module_reexport() {
752 check(
755 r"
756 //- /main.rs crate:main deps:lib
757 pub use lib::module as reexported_module;
758 //- /lib.rs crate:lib
759 pub mod module {
760 pub struct S;
761 }
762 ",
763 expect![[r#"
764 lib:
765 - module (t)
766 - module::S (t)
767 - module::S (v)
768 main:
769 - module::S (t)
770 - module::S (v)
771 - reexported_module (t)
772 "#]],
773 );
774 }
775
776 #[test]
777 fn cyclic_module_reexport() {
778 check(
780 r"
781 //- /lib.rs crate:lib
782 pub mod module {
783 pub struct S;
784 pub use super::sub::*;
785 }
786
787 pub mod sub {
788 pub use super::module;
789 }
790 ",
791 expect![[r#"
792 lib:
793 - module (t)
794 - module::S (t)
795 - module::S (v)
796 - module::module (t)
797 - sub (t)
798 - sub::module (t)
799 "#]],
800 );
801 }
802
803 #[test]
804 fn private_macro() {
805 check(
806 r"
807 //- /lib.rs crate:lib
808 macro_rules! private_macro {
809 () => {};
810 }
811 ",
812 expect![[r#"
813 lib:
814
815 "#]],
816 );
817 }
818
819 #[test]
820 fn namespacing() {
821 check(
822 r"
823 //- /lib.rs crate:lib
824 pub struct Thing; // t + v
825 #[macro_export]
826 macro_rules! Thing { // m
827 () => {};
828 }
829 ",
830 expect![[r#"
831 lib:
832 - Thing (m)
833 - Thing (t)
834 - Thing (v)
835 "#]],
836 );
837
838 check(
839 r"
840 //- /lib.rs crate:lib
841 pub mod Thing {} // t
842 #[macro_export]
843 macro_rules! Thing { // m
844 () => {};
845 }
846 ",
847 expect![[r#"
848 lib:
849 - Thing (m)
850 - Thing (t)
851 "#]],
852 );
853 }
854
855 #[test]
856 fn fuzzy_import_trait_and_assoc_items() {
857 cov_mark::check!(type_aliases_ignored);
858 let ra_fixture = r#"
859 //- /main.rs crate:main deps:dep
860 //- /dep.rs crate:dep
861 pub mod fmt {
862 pub trait Display {
863 type FmtTypeAlias;
864 const FMT_CONST: bool;
865
866 fn format_function();
867 fn format_method(&self);
868 }
869 }
870 "#;
871
872 check_search(
873 ra_fixture,
874 "main",
875 Query::new("fmt".to_owned()).fuzzy(),
876 expect![[r#"
877 dep::fmt (t)
878 dep::fmt::Display::FMT_CONST (a)
879 dep::fmt::Display::format_function (a)
880 dep::fmt::Display::format_method (a)
881 "#]],
882 );
883 }
884
885 #[test]
886 fn assoc_items_filtering() {
887 let ra_fixture = r#"
888 //- /main.rs crate:main deps:dep
889 //- /dep.rs crate:dep
890 pub mod fmt {
891 pub trait Display {
892 type FmtTypeAlias;
893 const FMT_CONST: bool;
894
895 fn format_function();
896 fn format_method(&self);
897 }
898 }
899 "#;
900
901 check_search(
902 ra_fixture,
903 "main",
904 Query::new("fmt".to_owned()).fuzzy().assoc_search_mode(AssocSearchMode::AssocItemsOnly),
905 expect![[r#"
906 dep::fmt::Display::FMT_CONST (a)
907 dep::fmt::Display::format_function (a)
908 dep::fmt::Display::format_method (a)
909 "#]],
910 );
911
912 check_search(
913 ra_fixture,
914 "main",
915 Query::new("fmt".to_owned()).fuzzy().assoc_search_mode(AssocSearchMode::Exclude),
916 expect![[r#"
917 dep::fmt (t)
918 "#]],
919 );
920 }
921
922 #[test]
923 fn search_mode() {
924 let ra_fixture = r#"
925//- /main.rs crate:main deps:dep
926//- /dep.rs crate:dep deps:tdep
927use tdep::fmt as fmt_dep;
928pub mod fmt {
929 pub trait Display {
930 fn fmt();
931 }
932}
933#[macro_export]
934macro_rules! Fmt {
935 () => {};
936}
937pub struct Fmt;
938
939pub fn format() {}
940pub fn no() {}
941
942//- /tdep.rs crate:tdep
943pub mod fmt {
944 pub struct NotImportableFromMain;
945}
946"#;
947
948 check_search(
949 ra_fixture,
950 "main",
951 Query::new("fmt".to_owned()).fuzzy(),
952 expect![[r#"
953 dep::Fmt (m)
954 dep::Fmt (t)
955 dep::Fmt (v)
956 dep::fmt (t)
957 dep::fmt::Display::fmt (a)
958 dep::format (f)
959 "#]],
960 );
961
962 check_search(
963 ra_fixture,
964 "main",
965 Query::new("fmt".to_owned()),
966 expect![[r#"
967 dep::Fmt (m)
968 dep::Fmt (t)
969 dep::Fmt (v)
970 dep::fmt (t)
971 dep::fmt::Display::fmt (a)
972 "#]],
973 );
974 }
975
976 #[test]
977 fn name_only() {
978 let ra_fixture = r#"
979 //- /main.rs crate:main deps:dep
980 //- /dep.rs crate:dep deps:tdep
981 use tdep::fmt as fmt_dep;
982 pub mod fmt {
983 pub trait Display {
984 fn fmt();
985 }
986 }
987 #[macro_export]
988 macro_rules! Fmt {
989 () => {};
990 }
991 pub struct Fmt;
992
993 pub fn format() {}
994 pub fn no() {}
995
996 //- /tdep.rs crate:tdep
997 pub mod fmt {
998 pub struct NotImportableFromMain;
999 }
1000 "#;
1001
1002 check_search(
1003 ra_fixture,
1004 "main",
1005 Query::new("fmt".to_owned()),
1006 expect![[r#"
1007 dep::Fmt (m)
1008 dep::Fmt (t)
1009 dep::Fmt (v)
1010 dep::fmt (t)
1011 dep::fmt::Display::fmt (a)
1012 "#]],
1013 );
1014 }
1015
1016 #[test]
1017 fn search_casing() {
1018 let ra_fixture = r#"
1019 //- /main.rs crate:main deps:dep
1020 //- /dep.rs crate:dep
1021
1022 pub struct fmt;
1023 pub struct FMT;
1024 "#;
1025
1026 check_search(
1027 ra_fixture,
1028 "main",
1029 Query::new("FMT".to_owned()),
1030 expect![[r#"
1031 dep::FMT (t)
1032 dep::FMT (v)
1033 dep::fmt (t)
1034 dep::fmt (v)
1035 "#]],
1036 );
1037
1038 check_search(
1039 ra_fixture,
1040 "main",
1041 Query::new("FMT".to_owned()).case_sensitive(),
1042 expect![[r#"
1043 dep::FMT (t)
1044 dep::FMT (v)
1045 "#]],
1046 );
1047 }
1048
1049 #[test]
1050 fn unicode_fn_name() {
1051 let ra_fixture = r#"
1052 //- /main.rs crate:main deps:dep
1053 //- /dep.rs crate:dep
1054 pub fn あい() {}
1055 "#;
1056
1057 check_search(
1058 ra_fixture,
1059 "main",
1060 Query::new("あ".to_owned()).fuzzy(),
1061 expect![[r#"
1062 dep::あい (f)
1063 "#]],
1064 );
1065 }
1066}