]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/import_map.rs
parameters.split_last()
[rust.git] / crates / hir_def / src / import_map.rs
1 //! A map of all publicly exported items in a crate.
2
3 use std::{fmt, hash::BuildHasherDefault, sync::Arc};
4
5 use base_db::CrateId;
6 use fst::{self, Streamer};
7 use hir_expand::name::Name;
8 use indexmap::{map::Entry, IndexMap};
9 use itertools::Itertools;
10 use rustc_hash::{FxHashSet, FxHasher};
11
12 use crate::{
13     db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
14     ModuleId, TraitId,
15 };
16
17 type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
18
19 /// Item import details stored in the `ImportMap`.
20 #[derive(Debug, Clone, Eq, PartialEq)]
21 pub struct ImportInfo {
22     /// A path that can be used to import the item, relative to the crate's root.
23     pub path: ImportPath,
24     /// The module containing this item.
25     pub container: ModuleId,
26     /// Whether the import is a trait associated item or not.
27     pub is_trait_assoc_item: bool,
28 }
29
30 #[derive(Debug, Clone, Eq, PartialEq)]
31 pub struct ImportPath {
32     pub segments: Vec<Name>,
33 }
34
35 impl fmt::Display for ImportPath {
36     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37         fmt::Display::fmt(&self.segments.iter().format("::"), f)
38     }
39 }
40
41 impl ImportPath {
42     fn len(&self) -> usize {
43         self.segments.len()
44     }
45 }
46
47 /// A map from publicly exported items to the path needed to import/name them from a downstream
48 /// crate.
49 ///
50 /// Reexports of items are taken into account, ie. if something is exported under multiple
51 /// names, the one with the shortest import path will be used.
52 ///
53 /// Note that all paths are relative to the containing crate's root, so the crate name still needs
54 /// to be prepended to the `ModPath` before the path is valid.
55 #[derive(Default)]
56 pub struct ImportMap {
57     map: FxIndexMap<ItemInNs, ImportInfo>,
58
59     /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
60     /// values returned by running `fst`.
61     ///
62     /// Since a path can refer to multiple items due to namespacing, we store all items with the
63     /// same path right after each other. This allows us to find all items after the FST gives us
64     /// the index of the first one.
65     importables: Vec<ItemInNs>,
66     fst: fst::Map<Vec<u8>>,
67 }
68
69 impl ImportMap {
70     pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
71         let _p = profile::span("import_map_query");
72
73         let mut import_map = collect_import_map(db, krate);
74
75         let mut importables = import_map
76             .map
77             .iter()
78             .map(|(item, info)| (item, fst_path(&info.path)))
79             .collect::<Vec<_>>();
80         importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
81
82         // Build the FST, taking care not to insert duplicate values.
83
84         let mut builder = fst::MapBuilder::memory();
85         let mut last_batch_start = 0;
86
87         for idx in 0..importables.len() {
88             let key = &importables[last_batch_start].1;
89             if let Some((_, fst_path)) = importables.get(idx + 1) {
90                 if key == fst_path {
91                     continue;
92                 }
93             }
94
95             let _ = builder.insert(key, last_batch_start as u64);
96
97             last_batch_start = idx + 1;
98         }
99
100         import_map.fst = builder.into_map();
101         import_map.importables = importables.iter().map(|&(&item, _)| item).collect();
102
103         Arc::new(import_map)
104     }
105
106     /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
107     pub fn path_of(&self, item: ItemInNs) -> Option<&ImportPath> {
108         self.import_info_for(item).map(|it| &it.path)
109     }
110
111     pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
112         self.map.get(&item)
113     }
114
115     fn collect_trait_assoc_items(
116         &mut self,
117         db: &dyn DefDatabase,
118         tr: TraitId,
119         is_type_in_ns: bool,
120         original_import_info: &ImportInfo,
121     ) {
122         let _p = profile::span("collect_trait_assoc_items");
123         for (assoc_item_name, item) in &db.trait_data(tr).items {
124             let module_def_id = match item {
125                 AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
126                 AssocItemId::ConstId(c) => ModuleDefId::from(*c),
127                 // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
128                 // qualifier, ergo no need to store it for imports in import_map
129                 AssocItemId::TypeAliasId(_) => {
130                     cov_mark::hit!(type_aliases_ignored);
131                     continue;
132                 }
133             };
134             let assoc_item = if is_type_in_ns {
135                 ItemInNs::Types(module_def_id)
136             } else {
137                 ItemInNs::Values(module_def_id)
138             };
139
140             let mut assoc_item_info = original_import_info.clone();
141             assoc_item_info.path.segments.push(assoc_item_name.to_owned());
142             assoc_item_info.is_trait_assoc_item = true;
143             self.map.insert(assoc_item, assoc_item_info);
144         }
145     }
146 }
147
148 fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
149     let _p = profile::span("collect_import_map");
150
151     let def_map = db.crate_def_map(krate);
152     let mut import_map = ImportMap::default();
153
154     // We look only into modules that are public(ly reexported), starting with the crate root.
155     let empty = ImportPath { segments: vec![] };
156     let root = def_map.module_id(def_map.root());
157     let mut worklist = vec![(root, empty)];
158     while let Some((module, mod_path)) = worklist.pop() {
159         let ext_def_map;
160         let mod_data = if module.krate == krate {
161             &def_map[module.local_id]
162         } else {
163             // The crate might reexport a module defined in another crate.
164             ext_def_map = module.def_map(db);
165             &ext_def_map[module.local_id]
166         };
167
168         let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
169             let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
170             if per_ns.is_none() {
171                 None
172             } else {
173                 Some((name, per_ns))
174             }
175         });
176
177         for (name, per_ns) in visible_items {
178             let mk_path = || {
179                 let mut path = mod_path.clone();
180                 path.segments.push(name.clone());
181                 path
182             };
183
184             for item in per_ns.iter_items() {
185                 let path = mk_path();
186                 let path_len = path.len();
187                 let import_info =
188                     ImportInfo { path, container: module, is_trait_assoc_item: false };
189
190                 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
191                     import_map.collect_trait_assoc_items(
192                         db,
193                         tr,
194                         matches!(item, ItemInNs::Types(_)),
195                         &import_info,
196                     );
197                 }
198
199                 match import_map.map.entry(item) {
200                     Entry::Vacant(entry) => {
201                         entry.insert(import_info);
202                     }
203                     Entry::Occupied(mut entry) => {
204                         // If the new path is shorter, prefer that one.
205                         if path_len < entry.get().path.len() {
206                             *entry.get_mut() = import_info;
207                         } else {
208                             continue;
209                         }
210                     }
211                 }
212
213                 // If we've just added a path to a module, descend into it. We might traverse
214                 // modules multiple times, but only if the new path to it is shorter than the
215                 // first (else we `continue` above).
216                 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
217                     worklist.push((mod_id, mk_path()));
218                 }
219             }
220         }
221     }
222
223     import_map
224 }
225
226 impl PartialEq for ImportMap {
227     fn eq(&self, other: &Self) -> bool {
228         // `fst` and `importables` are built from `map`, so we don't need to compare them.
229         self.map == other.map
230     }
231 }
232
233 impl Eq for ImportMap {}
234
235 impl fmt::Debug for ImportMap {
236     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237         let mut importable_paths: Vec<_> = self
238             .map
239             .iter()
240             .map(|(item, info)| {
241                 let ns = match item {
242                     ItemInNs::Types(_) => "t",
243                     ItemInNs::Values(_) => "v",
244                     ItemInNs::Macros(_) => "m",
245                 };
246                 format!("- {} ({})", info.path, ns)
247             })
248             .collect();
249
250         importable_paths.sort();
251         f.write_str(&importable_paths.join("\n"))
252     }
253 }
254
255 fn fst_path(path: &ImportPath) -> String {
256     let _p = profile::span("fst_path");
257     let mut s = path.to_string();
258     s.make_ascii_lowercase();
259     s
260 }
261
262 #[derive(Debug, Eq, PartialEq, Hash)]
263 pub enum ImportKind {
264     Module,
265     Function,
266     Adt,
267     EnumVariant,
268     Const,
269     Static,
270     Trait,
271     TypeAlias,
272     BuiltinType,
273     AssociatedItem,
274 }
275
276 /// A way to match import map contents against the search query.
277 #[derive(Debug)]
278 pub enum SearchMode {
279     /// Import map entry should strictly match the query string.
280     Equals,
281     /// Import map entry should contain the query string.
282     Contains,
283     /// Import map entry should contain all letters from the query string,
284     /// in the same order, but not necessary adjacent.
285     Fuzzy,
286 }
287
288 #[derive(Debug)]
289 pub struct Query {
290     query: String,
291     lowercased: String,
292     name_only: bool,
293     assoc_items_only: bool,
294     search_mode: SearchMode,
295     case_sensitive: bool,
296     limit: usize,
297     exclude_import_kinds: FxHashSet<ImportKind>,
298 }
299
300 impl Query {
301     pub fn new(query: String) -> Self {
302         let lowercased = query.to_lowercase();
303         Self {
304             query,
305             lowercased,
306             name_only: false,
307             assoc_items_only: false,
308             search_mode: SearchMode::Contains,
309             case_sensitive: false,
310             limit: usize::max_value(),
311             exclude_import_kinds: FxHashSet::default(),
312         }
313     }
314
315     /// Matches entries' names only, ignoring the rest of
316     /// the qualifier.
317     /// Example: for `std::marker::PhantomData`, the name is `PhantomData`.
318     pub fn name_only(self) -> Self {
319         Self { name_only: true, ..self }
320     }
321
322     /// Matches only the entries that are associated items, ignoring the rest.
323     pub fn assoc_items_only(self) -> Self {
324         Self { assoc_items_only: true, ..self }
325     }
326
327     /// Specifies the way to search for the entries using the query.
328     pub fn search_mode(self, search_mode: SearchMode) -> Self {
329         Self { search_mode, ..self }
330     }
331
332     /// Limits the returned number of items to `limit`.
333     pub fn limit(self, limit: usize) -> Self {
334         Self { limit, ..self }
335     }
336
337     /// Respect casing of the query string when matching.
338     pub fn case_sensitive(self) -> Self {
339         Self { case_sensitive: true, ..self }
340     }
341
342     /// Do not include imports of the specified kind in the search results.
343     pub fn exclude_import_kind(mut self, import_kind: ImportKind) -> Self {
344         self.exclude_import_kinds.insert(import_kind);
345         self
346     }
347
348     fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
349         let _p = profile::span("import_map::Query::import_matches");
350         if import.is_trait_assoc_item {
351             if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
352                 return false;
353             }
354         } else if self.assoc_items_only {
355             return false;
356         }
357
358         let mut input = if import.is_trait_assoc_item || self.name_only {
359             import.path.segments.last().unwrap().to_string()
360         } else {
361             import.path.to_string()
362         };
363         if enforce_lowercase || !self.case_sensitive {
364             input.make_ascii_lowercase();
365         }
366
367         let query_string =
368             if !enforce_lowercase && self.case_sensitive { &self.query } else { &self.lowercased };
369
370         match self.search_mode {
371             SearchMode::Equals => &input == query_string,
372             SearchMode::Contains => input.contains(query_string),
373             SearchMode::Fuzzy => {
374                 let mut unchecked_query_chars = query_string.chars();
375                 let mut mismatching_query_char = unchecked_query_chars.next();
376
377                 for input_char in input.chars() {
378                     match mismatching_query_char {
379                         None => return true,
380                         Some(matching_query_char) if matching_query_char == input_char => {
381                             mismatching_query_char = unchecked_query_chars.next();
382                         }
383                         _ => (),
384                     }
385                 }
386                 mismatching_query_char.is_none()
387             }
388         }
389     }
390 }
391
392 /// Searches dependencies of `krate` for an importable path matching `query`.
393 ///
394 /// This returns a list of items that could be imported from dependencies of `krate`.
395 pub fn search_dependencies<'a>(
396     db: &'a dyn DefDatabase,
397     krate: CrateId,
398     query: Query,
399 ) -> FxHashSet<ItemInNs> {
400     let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query));
401
402     let graph = db.crate_graph();
403     let import_maps: Vec<_> =
404         graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
405
406     let automaton = fst::automaton::Subsequence::new(&query.lowercased);
407
408     let mut op = fst::map::OpBuilder::new();
409     for map in &import_maps {
410         op = op.add(map.fst.search(&automaton));
411     }
412
413     let mut stream = op.union();
414
415     let mut all_indexed_values = FxHashSet::default();
416     while let Some((_, indexed_values)) = stream.next() {
417         all_indexed_values.extend(indexed_values.iter().copied());
418     }
419
420     let mut res = FxHashSet::default();
421     for indexed_value in all_indexed_values {
422         let import_map = &import_maps[indexed_value.index];
423         let importables = &import_map.importables[indexed_value.value as usize..];
424
425         let common_importable_data = &import_map.map[&importables[0]];
426         if !query.import_matches(common_importable_data, true) {
427             continue;
428         }
429
430         // Path shared by the importable items in this group.
431         let common_importables_path_fst = fst_path(&common_importable_data.path);
432         // Add the items from this `ModPath` group. Those are all subsequent items in
433         // `importables` whose paths match `path`.
434         let iter = importables
435             .iter()
436             .copied()
437             .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path))
438             .filter(|&item| match item_import_kind(item) {
439                 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
440                 None => true,
441             })
442             .filter(|item| {
443                 !query.case_sensitive // we've already checked the common importables path case-insensitively
444                         || query.import_matches(&import_map.map[item], false)
445             });
446         res.extend(iter);
447
448         if res.len() >= query.limit {
449             return res;
450         }
451     }
452
453     res
454 }
455
456 fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
457     Some(match item.as_module_def_id()? {
458         ModuleDefId::ModuleId(_) => ImportKind::Module,
459         ModuleDefId::FunctionId(_) => ImportKind::Function,
460         ModuleDefId::AdtId(_) => ImportKind::Adt,
461         ModuleDefId::EnumVariantId(_) => ImportKind::EnumVariant,
462         ModuleDefId::ConstId(_) => ImportKind::Const,
463         ModuleDefId::StaticId(_) => ImportKind::Static,
464         ModuleDefId::TraitId(_) => ImportKind::Trait,
465         ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias,
466         ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType,
467     })
468 }
469
470 #[cfg(test)]
471 mod tests {
472     use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
473     use expect_test::{expect, Expect};
474
475     use crate::{test_db::TestDB, ItemContainerId, Lookup};
476
477     use super::*;
478
479     fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) {
480         let db = TestDB::with_files(ra_fixture);
481         let crate_graph = db.crate_graph();
482         let krate = crate_graph
483             .iter()
484             .find(|krate| {
485                 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
486                     == Some(crate_name.to_string())
487             })
488             .unwrap();
489
490         let actual = search_dependencies(db.upcast(), krate, query)
491             .into_iter()
492             .filter_map(|dependency| {
493                 let dependency_krate = dependency.krate(db.upcast())?;
494                 let dependency_imports = db.import_map(dependency_krate);
495
496                 let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
497                     Some(assoc_item_path) => (assoc_item_path, "a"),
498                     None => (
499                         dependency_imports.path_of(dependency)?.to_string(),
500                         match dependency {
501                             ItemInNs::Types(ModuleDefId::FunctionId(_))
502                             | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
503                             ItemInNs::Types(_) => "t",
504                             ItemInNs::Values(_) => "v",
505                             ItemInNs::Macros(_) => "m",
506                         },
507                     ),
508                 };
509
510                 Some(format!(
511                     "{}::{} ({})\n",
512                     crate_graph[dependency_krate].display_name.as_ref()?,
513                     path,
514                     mark
515                 ))
516             })
517             .collect::<String>();
518         expect.assert_eq(&actual)
519     }
520
521     fn assoc_item_path(
522         db: &dyn DefDatabase,
523         dependency_imports: &ImportMap,
524         dependency: ItemInNs,
525     ) -> Option<String> {
526         let dependency_assoc_item_id = match dependency {
527             ItemInNs::Types(ModuleDefId::FunctionId(id))
528             | ItemInNs::Values(ModuleDefId::FunctionId(id)) => AssocItemId::from(id),
529             ItemInNs::Types(ModuleDefId::ConstId(id))
530             | ItemInNs::Values(ModuleDefId::ConstId(id)) => AssocItemId::from(id),
531             ItemInNs::Types(ModuleDefId::TypeAliasId(id))
532             | ItemInNs::Values(ModuleDefId::TypeAliasId(id)) => AssocItemId::from(id),
533             _ => return None,
534         };
535
536         let trait_ = assoc_to_trait(db, dependency)?;
537         if let ModuleDefId::TraitId(tr) = trait_.as_module_def_id()? {
538             let trait_data = db.trait_data(tr);
539             let assoc_item_name =
540                 trait_data.items.iter().find_map(|(assoc_item_name, assoc_item_id)| {
541                     if &dependency_assoc_item_id == assoc_item_id {
542                         Some(assoc_item_name)
543                     } else {
544                         None
545                     }
546                 })?;
547             return Some(format!("{}::{}", dependency_imports.path_of(trait_)?, assoc_item_name));
548         }
549         None
550     }
551
552     fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
553         let assoc: AssocItemId = match item {
554             ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
555                 ModuleDefId::TypeAliasId(it) => it.into(),
556                 ModuleDefId::FunctionId(it) => it.into(),
557                 ModuleDefId::ConstId(it) => it.into(),
558                 _ => return None,
559             },
560             _ => return None,
561         };
562
563         let container = match assoc {
564             AssocItemId::FunctionId(it) => it.lookup(db).container,
565             AssocItemId::ConstId(it) => it.lookup(db).container,
566             AssocItemId::TypeAliasId(it) => it.lookup(db).container,
567         };
568
569         match container {
570             ItemContainerId::TraitId(it) => Some(ItemInNs::Types(it.into())),
571             _ => None,
572         }
573     }
574
575     fn check(ra_fixture: &str, expect: Expect) {
576         let db = TestDB::with_files(ra_fixture);
577         let crate_graph = db.crate_graph();
578
579         let actual = crate_graph
580             .iter()
581             .filter_map(|krate| {
582                 let cdata = &crate_graph[krate];
583                 let name = cdata.display_name.as_ref()?;
584
585                 let map = db.import_map(krate);
586
587                 Some(format!("{}:\n{:?}\n", name, map))
588             })
589             .collect::<String>();
590
591         expect.assert_eq(&actual)
592     }
593
594     #[test]
595     fn smoke() {
596         check(
597             r"
598             //- /main.rs crate:main deps:lib
599
600             mod private {
601                 pub use lib::Pub;
602                 pub struct InPrivateModule;
603             }
604
605             pub mod publ1 {
606                 use lib::Pub;
607             }
608
609             pub mod real_pub {
610                 pub use lib::Pub;
611             }
612             pub mod real_pu2 { // same path length as above
613                 pub use lib::Pub;
614             }
615
616             //- /lib.rs crate:lib
617             pub struct Pub {}
618             pub struct Pub2; // t + v
619             struct Priv;
620         ",
621             expect![[r#"
622                 main:
623                 - publ1 (t)
624                 - real_pu2 (t)
625                 - real_pub (t)
626                 - real_pub::Pub (t)
627                 lib:
628                 - Pub (t)
629                 - Pub2 (t)
630                 - Pub2 (v)
631             "#]],
632         );
633     }
634
635     #[test]
636     fn prefers_shortest_path() {
637         check(
638             r"
639             //- /main.rs crate:main
640
641             pub mod sub {
642                 pub mod subsub {
643                     pub struct Def {}
644                 }
645
646                 pub use super::sub::subsub::Def;
647             }
648         ",
649             expect![[r#"
650                 main:
651                 - sub (t)
652                 - sub::Def (t)
653                 - sub::subsub (t)
654             "#]],
655         );
656     }
657
658     #[test]
659     fn type_reexport_cross_crate() {
660         // Reexports need to be visible from a crate, even if the original crate exports the item
661         // at a shorter path.
662         check(
663             r"
664             //- /main.rs crate:main deps:lib
665             pub mod m {
666                 pub use lib::S;
667             }
668             //- /lib.rs crate:lib
669             pub struct S;
670         ",
671             expect![[r#"
672                 main:
673                 - m (t)
674                 - m::S (t)
675                 - m::S (v)
676                 lib:
677                 - S (t)
678                 - S (v)
679             "#]],
680         );
681     }
682
683     #[test]
684     fn macro_reexport() {
685         check(
686             r"
687             //- /main.rs crate:main deps:lib
688             pub mod m {
689                 pub use lib::pub_macro;
690             }
691             //- /lib.rs crate:lib
692             #[macro_export]
693             macro_rules! pub_macro {
694                 () => {};
695             }
696         ",
697             expect![[r#"
698                 main:
699                 - m (t)
700                 - m::pub_macro (m)
701                 lib:
702                 - pub_macro (m)
703             "#]],
704         );
705     }
706
707     #[test]
708     fn module_reexport() {
709         // Reexporting modules from a dependency adds all contents to the import map.
710         check(
711             r"
712             //- /main.rs crate:main deps:lib
713             pub use lib::module as reexported_module;
714             //- /lib.rs crate:lib
715             pub mod module {
716                 pub struct S;
717             }
718         ",
719             expect![[r#"
720                 main:
721                 - reexported_module (t)
722                 - reexported_module::S (t)
723                 - reexported_module::S (v)
724                 lib:
725                 - module (t)
726                 - module::S (t)
727                 - module::S (v)
728             "#]],
729         );
730     }
731
732     #[test]
733     fn cyclic_module_reexport() {
734         // A cyclic reexport does not hang.
735         check(
736             r"
737             //- /lib.rs crate:lib
738             pub mod module {
739                 pub struct S;
740                 pub use super::sub::*;
741             }
742
743             pub mod sub {
744                 pub use super::module;
745             }
746         ",
747             expect![[r#"
748                 lib:
749                 - module (t)
750                 - module::S (t)
751                 - module::S (v)
752                 - sub (t)
753             "#]],
754         );
755     }
756
757     #[test]
758     fn private_macro() {
759         check(
760             r"
761             //- /lib.rs crate:lib
762             macro_rules! private_macro {
763                 () => {};
764             }
765         ",
766             expect![[r#"
767                 lib:
768
769             "#]],
770         );
771     }
772
773     #[test]
774     fn namespacing() {
775         check(
776             r"
777             //- /lib.rs crate:lib
778             pub struct Thing;     // t + v
779             #[macro_export]
780             macro_rules! Thing {  // m
781                 () => {};
782             }
783         ",
784             expect![[r#"
785                 lib:
786                 - Thing (m)
787                 - Thing (t)
788                 - Thing (v)
789             "#]],
790         );
791
792         check(
793             r"
794             //- /lib.rs crate:lib
795             pub mod Thing {}      // t
796             #[macro_export]
797             macro_rules! Thing {  // m
798                 () => {};
799             }
800         ",
801             expect![[r#"
802                 lib:
803                 - Thing (m)
804                 - Thing (t)
805             "#]],
806         );
807     }
808
809     #[test]
810     fn fuzzy_import_trait_and_assoc_items() {
811         cov_mark::check!(type_aliases_ignored);
812         let ra_fixture = r#"
813         //- /main.rs crate:main deps:dep
814         //- /dep.rs crate:dep
815         pub mod fmt {
816             pub trait Display {
817                 type FmtTypeAlias;
818                 const FMT_CONST: bool;
819
820                 fn format_function();
821                 fn format_method(&self);
822             }
823         }
824     "#;
825
826         check_search(
827             ra_fixture,
828             "main",
829             Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
830             expect![[r#"
831                 dep::fmt (t)
832                 dep::fmt::Display::format_method (a)
833                 dep::fmt::Display (t)
834                 dep::fmt::Display::FMT_CONST (a)
835                 dep::fmt::Display::format_function (a)
836             "#]],
837         );
838     }
839
840     #[test]
841     fn assoc_items_filtering() {
842         let ra_fixture = r#"
843         //- /main.rs crate:main deps:dep
844         //- /dep.rs crate:dep
845         pub mod fmt {
846             pub trait Display {
847                 type FmtTypeAlias;
848                 const FMT_CONST: bool;
849
850                 fn format_function();
851                 fn format_method(&self);
852             }
853         }
854     "#;
855
856         check_search(
857             ra_fixture,
858             "main",
859             Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(),
860             expect![[r#"
861             dep::fmt::Display::format_method (a)
862             dep::fmt::Display::FMT_CONST (a)
863             dep::fmt::Display::format_function (a)
864         "#]],
865         );
866
867         check_search(
868             ra_fixture,
869             "main",
870             Query::new("fmt".to_string())
871                 .search_mode(SearchMode::Fuzzy)
872                 .exclude_import_kind(ImportKind::AssociatedItem),
873             expect![[r#"
874             dep::fmt (t)
875             dep::fmt::Display (t)
876         "#]],
877         );
878
879         check_search(
880             ra_fixture,
881             "main",
882             Query::new("fmt".to_string())
883                 .search_mode(SearchMode::Fuzzy)
884                 .assoc_items_only()
885                 .exclude_import_kind(ImportKind::AssociatedItem),
886             expect![[r#""#]],
887         );
888     }
889
890     #[test]
891     fn search_mode() {
892         let ra_fixture = r#"
893             //- /main.rs crate:main deps:dep
894             //- /dep.rs crate:dep deps:tdep
895             use tdep::fmt as fmt_dep;
896             pub mod fmt {
897                 pub trait Display {
898                     fn fmt();
899                 }
900             }
901             #[macro_export]
902             macro_rules! Fmt {
903                 () => {};
904             }
905             pub struct Fmt;
906
907             pub fn format() {}
908             pub fn no() {}
909
910             //- /tdep.rs crate:tdep
911             pub mod fmt {
912                 pub struct NotImportableFromMain;
913             }
914         "#;
915
916         check_search(
917             ra_fixture,
918             "main",
919             Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
920             expect![[r#"
921                 dep::fmt (t)
922                 dep::format (f)
923                 dep::Fmt (v)
924                 dep::Fmt (m)
925                 dep::Fmt (t)
926                 dep::fmt::Display::fmt (a)
927                 dep::fmt::Display (t)
928             "#]],
929         );
930
931         check_search(
932             ra_fixture,
933             "main",
934             Query::new("fmt".to_string()).search_mode(SearchMode::Equals),
935             expect![[r#"
936                 dep::fmt (t)
937                 dep::Fmt (v)
938                 dep::Fmt (m)
939                 dep::Fmt (t)
940                 dep::fmt::Display::fmt (a)
941             "#]],
942         );
943
944         check_search(
945             ra_fixture,
946             "main",
947             Query::new("fmt".to_string()).search_mode(SearchMode::Contains),
948             expect![[r#"
949                 dep::fmt (t)
950                 dep::Fmt (v)
951                 dep::Fmt (m)
952                 dep::Fmt (t)
953                 dep::fmt::Display::fmt (a)
954                 dep::fmt::Display (t)
955             "#]],
956         );
957     }
958
959     #[test]
960     fn name_only() {
961         let ra_fixture = r#"
962             //- /main.rs crate:main deps:dep
963             //- /dep.rs crate:dep deps:tdep
964             use tdep::fmt as fmt_dep;
965             pub mod fmt {
966                 pub trait Display {
967                     fn fmt();
968                 }
969             }
970             #[macro_export]
971             macro_rules! Fmt {
972                 () => {};
973             }
974             pub struct Fmt;
975
976             pub fn format() {}
977             pub fn no() {}
978
979             //- /tdep.rs crate:tdep
980             pub mod fmt {
981                 pub struct NotImportableFromMain;
982             }
983         "#;
984
985         check_search(
986             ra_fixture,
987             "main",
988             Query::new("fmt".to_string()),
989             expect![[r#"
990                 dep::fmt (t)
991                 dep::Fmt (v)
992                 dep::Fmt (m)
993                 dep::Fmt (t)
994                 dep::fmt::Display::fmt (a)
995                 dep::fmt::Display (t)
996             "#]],
997         );
998
999         check_search(
1000             ra_fixture,
1001             "main",
1002             Query::new("fmt".to_string()).name_only(),
1003             expect![[r#"
1004                 dep::fmt (t)
1005                 dep::Fmt (v)
1006                 dep::Fmt (m)
1007                 dep::Fmt (t)
1008                 dep::fmt::Display::fmt (a)
1009             "#]],
1010         );
1011     }
1012
1013     #[test]
1014     fn search_casing() {
1015         let ra_fixture = r#"
1016             //- /main.rs crate:main deps:dep
1017             //- /dep.rs crate:dep
1018
1019             pub struct fmt;
1020             pub struct FMT;
1021         "#;
1022
1023         check_search(
1024             ra_fixture,
1025             "main",
1026             Query::new("FMT".to_string()),
1027             expect![[r#"
1028                 dep::fmt (t)
1029                 dep::FMT (v)
1030                 dep::fmt (v)
1031                 dep::FMT (t)
1032             "#]],
1033         );
1034
1035         check_search(
1036             ra_fixture,
1037             "main",
1038             Query::new("FMT".to_string()).case_sensitive(),
1039             expect![[r#"
1040                 dep::FMT (t)
1041                 dep::FMT (v)
1042             "#]],
1043         );
1044     }
1045
1046     #[test]
1047     fn search_limit() {
1048         check_search(
1049             r#"
1050         //- /main.rs crate:main deps:dep
1051         //- /dep.rs crate:dep
1052         pub mod fmt {
1053             pub trait Display {
1054                 fn fmt();
1055             }
1056         }
1057         #[macro_export]
1058         macro_rules! Fmt {
1059             () => {};
1060         }
1061         pub struct Fmt;
1062
1063         pub fn format() {}
1064         pub fn no() {}
1065     "#,
1066             "main",
1067             Query::new("".to_string()).limit(2),
1068             expect![[r#"
1069                 dep::fmt (t)
1070                 dep::Fmt (m)
1071                 dep::Fmt (t)
1072                 dep::Fmt (v)
1073             "#]],
1074         );
1075     }
1076
1077     #[test]
1078     fn search_exclusions() {
1079         let ra_fixture = r#"
1080             //- /main.rs crate:main deps:dep
1081             //- /dep.rs crate:dep
1082
1083             pub struct fmt;
1084             pub struct FMT;
1085         "#;
1086
1087         check_search(
1088             ra_fixture,
1089             "main",
1090             Query::new("FMT".to_string()),
1091             expect![[r#"
1092                 dep::fmt (t)
1093                 dep::FMT (v)
1094                 dep::fmt (v)
1095                 dep::FMT (t)
1096             "#]],
1097         );
1098
1099         check_search(
1100             ra_fixture,
1101             "main",
1102             Query::new("FMT".to_string()).exclude_import_kind(ImportKind::Adt),
1103             expect![[r#""#]],
1104         );
1105     }
1106 }