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