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