]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/import_map.rs
Merge #7092
[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     use stdx::format_to;
436
437     use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup};
438
439     use super::*;
440
441     fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) {
442         let db = TestDB::with_files(ra_fixture);
443         let crate_graph = db.crate_graph();
444         let krate = crate_graph
445             .iter()
446             .find(|krate| {
447                 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
448                     == Some(crate_name.to_string())
449             })
450             .unwrap();
451
452         let actual = search_dependencies(db.upcast(), krate, query)
453             .into_iter()
454             .filter_map(|item| {
455                 let mark = match item {
456                     ItemInNs::Types(ModuleDefId::FunctionId(_))
457                     | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
458                     ItemInNs::Types(_) => "t",
459                     ItemInNs::Values(_) => "v",
460                     ItemInNs::Macros(_) => "m",
461                 };
462                 item.krate(db.upcast()).map(|krate| {
463                     let map = db.import_map(krate);
464
465                     let path = match assoc_to_trait(&db, item) {
466                         Some(trait_) => {
467                             let mut full_path = map.path_of(trait_).unwrap().to_string();
468                             if let ItemInNs::Types(ModuleDefId::FunctionId(function_id))
469                             | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item
470                             {
471                                 format_to!(
472                                     full_path,
473                                     "::{}",
474                                     FunctionData::fn_data_query(&db, function_id).name,
475                                 );
476                             }
477                             full_path
478                         }
479                         None => map.path_of(item).unwrap().to_string(),
480                     };
481
482                     format!(
483                         "{}::{} ({})\n",
484                         crate_graph[krate].display_name.as_ref().unwrap(),
485                         path,
486                         mark
487                     )
488                 })
489             })
490             .collect::<String>();
491         expect.assert_eq(&actual)
492     }
493
494     fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
495         let assoc: AssocItemId = match item {
496             ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
497                 ModuleDefId::TypeAliasId(it) => it.into(),
498                 ModuleDefId::FunctionId(it) => it.into(),
499                 ModuleDefId::ConstId(it) => it.into(),
500                 _ => return None,
501             },
502             _ => return None,
503         };
504
505         let container = match assoc {
506             AssocItemId::FunctionId(it) => it.lookup(db).container,
507             AssocItemId::ConstId(it) => it.lookup(db).container,
508             AssocItemId::TypeAliasId(it) => it.lookup(db).container,
509         };
510
511         match container {
512             AssocContainerId::TraitId(it) => Some(ItemInNs::Types(it.into())),
513             _ => None,
514         }
515     }
516
517     fn check(ra_fixture: &str, expect: Expect) {
518         let db = TestDB::with_files(ra_fixture);
519         let crate_graph = db.crate_graph();
520
521         let actual = crate_graph
522             .iter()
523             .filter_map(|krate| {
524                 let cdata = &crate_graph[krate];
525                 let name = cdata.display_name.as_ref()?;
526
527                 let map = db.import_map(krate);
528
529                 Some(format!("{}:\n{:?}\n", name, map))
530             })
531             .collect::<String>();
532
533         expect.assert_eq(&actual)
534     }
535
536     #[test]
537     fn smoke() {
538         check(
539             r"
540             //- /main.rs crate:main deps:lib
541
542             mod private {
543                 pub use lib::Pub;
544                 pub struct InPrivateModule;
545             }
546
547             pub mod publ1 {
548                 use lib::Pub;
549             }
550
551             pub mod real_pub {
552                 pub use lib::Pub;
553             }
554             pub mod real_pu2 { // same path length as above
555                 pub use lib::Pub;
556             }
557
558             //- /lib.rs crate:lib
559             pub struct Pub {}
560             pub struct Pub2; // t + v
561             struct Priv;
562         ",
563             expect![[r#"
564                 main:
565                 - publ1 (t)
566                 - real_pu2 (t)
567                 - real_pub (t)
568                 - real_pub::Pub (t)
569                 lib:
570                 - Pub (t)
571                 - Pub2 (t)
572                 - Pub2 (v)
573             "#]],
574         );
575     }
576
577     #[test]
578     fn prefers_shortest_path() {
579         check(
580             r"
581             //- /main.rs crate:main
582
583             pub mod sub {
584                 pub mod subsub {
585                     pub struct Def {}
586                 }
587
588                 pub use super::sub::subsub::Def;
589             }
590         ",
591             expect![[r#"
592                 main:
593                 - sub (t)
594                 - sub::Def (t)
595                 - sub::subsub (t)
596             "#]],
597         );
598     }
599
600     #[test]
601     fn type_reexport_cross_crate() {
602         // Reexports need to be visible from a crate, even if the original crate exports the item
603         // at a shorter path.
604         check(
605             r"
606             //- /main.rs crate:main deps:lib
607             pub mod m {
608                 pub use lib::S;
609             }
610             //- /lib.rs crate:lib
611             pub struct S;
612         ",
613             expect![[r#"
614                 main:
615                 - m (t)
616                 - m::S (t)
617                 - m::S (v)
618                 lib:
619                 - S (t)
620                 - S (v)
621             "#]],
622         );
623     }
624
625     #[test]
626     fn macro_reexport() {
627         check(
628             r"
629             //- /main.rs crate:main deps:lib
630             pub mod m {
631                 pub use lib::pub_macro;
632             }
633             //- /lib.rs crate:lib
634             #[macro_export]
635             macro_rules! pub_macro {
636                 () => {};
637             }
638         ",
639             expect![[r#"
640                 main:
641                 - m (t)
642                 - m::pub_macro (m)
643                 lib:
644                 - pub_macro (m)
645             "#]],
646         );
647     }
648
649     #[test]
650     fn module_reexport() {
651         // Reexporting modules from a dependency adds all contents to the import map.
652         check(
653             r"
654             //- /main.rs crate:main deps:lib
655             pub use lib::module as reexported_module;
656             //- /lib.rs crate:lib
657             pub mod module {
658                 pub struct S;
659             }
660         ",
661             expect![[r#"
662                 main:
663                 - reexported_module (t)
664                 - reexported_module::S (t)
665                 - reexported_module::S (v)
666                 lib:
667                 - module (t)
668                 - module::S (t)
669                 - module::S (v)
670             "#]],
671         );
672     }
673
674     #[test]
675     fn cyclic_module_reexport() {
676         // A cyclic reexport does not hang.
677         check(
678             r"
679             //- /lib.rs crate:lib
680             pub mod module {
681                 pub struct S;
682                 pub use super::sub::*;
683             }
684
685             pub mod sub {
686                 pub use super::module;
687             }
688         ",
689             expect![[r#"
690                 lib:
691                 - module (t)
692                 - module::S (t)
693                 - module::S (v)
694                 - sub (t)
695             "#]],
696         );
697     }
698
699     #[test]
700     fn private_macro() {
701         check(
702             r"
703             //- /lib.rs crate:lib
704             macro_rules! private_macro {
705                 () => {};
706             }
707         ",
708             expect![[r#"
709                 lib:
710
711             "#]],
712         );
713     }
714
715     #[test]
716     fn namespacing() {
717         check(
718             r"
719             //- /lib.rs crate:lib
720             pub struct Thing;     // t + v
721             #[macro_export]
722             macro_rules! Thing {  // m
723                 () => {};
724             }
725         ",
726             expect![[r#"
727                 lib:
728                 - Thing (m)
729                 - Thing (t)
730                 - Thing (v)
731             "#]],
732         );
733
734         check(
735             r"
736             //- /lib.rs crate:lib
737             pub mod Thing {}      // t
738             #[macro_export]
739             macro_rules! Thing {  // m
740                 () => {};
741             }
742         ",
743             expect![[r#"
744                 lib:
745                 - Thing (m)
746                 - Thing (t)
747             "#]],
748         );
749     }
750
751     #[test]
752     fn search_mode() {
753         let ra_fixture = r#"
754             //- /main.rs crate:main deps:dep
755             //- /dep.rs crate:dep deps:tdep
756             use tdep::fmt as fmt_dep;
757             pub mod fmt {
758                 pub trait Display {
759                     fn fmt();
760                 }
761             }
762             #[macro_export]
763             macro_rules! Fmt {
764                 () => {};
765             }
766             pub struct Fmt;
767
768             pub fn format() {}
769             pub fn no() {}
770
771             //- /tdep.rs crate:tdep
772             pub mod fmt {
773                 pub struct NotImportableFromMain;
774             }
775         "#;
776
777         check_search(
778             ra_fixture,
779             "main",
780             Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
781             expect![[r#"
782                 dep::fmt (t)
783                 dep::Fmt (t)
784                 dep::Fmt (v)
785                 dep::Fmt (m)
786                 dep::fmt::Display (t)
787                 dep::format (f)
788                 dep::fmt::Display::fmt (f)
789             "#]],
790         );
791
792         check_search(
793             ra_fixture,
794             "main",
795             Query::new("fmt".to_string()).search_mode(SearchMode::Equals),
796             expect![[r#"
797                 dep::fmt (t)
798                 dep::Fmt (t)
799                 dep::Fmt (v)
800                 dep::Fmt (m)
801                 dep::fmt::Display::fmt (f)
802             "#]],
803         );
804
805         check_search(
806             ra_fixture,
807             "main",
808             Query::new("fmt".to_string()).search_mode(SearchMode::Contains),
809             expect![[r#"
810                 dep::fmt (t)
811                 dep::Fmt (t)
812                 dep::Fmt (v)
813                 dep::Fmt (m)
814                 dep::fmt::Display (t)
815                 dep::fmt::Display::fmt (f)
816             "#]],
817         );
818     }
819
820     #[test]
821     fn name_only() {
822         let ra_fixture = r#"
823             //- /main.rs crate:main deps:dep
824             //- /dep.rs crate:dep deps:tdep
825             use tdep::fmt as fmt_dep;
826             pub mod fmt {
827                 pub trait Display {
828                     fn fmt();
829                 }
830             }
831             #[macro_export]
832             macro_rules! Fmt {
833                 () => {};
834             }
835             pub struct Fmt;
836
837             pub fn format() {}
838             pub fn no() {}
839
840             //- /tdep.rs crate:tdep
841             pub mod fmt {
842                 pub struct NotImportableFromMain;
843             }
844         "#;
845
846         check_search(
847             ra_fixture,
848             "main",
849             Query::new("fmt".to_string()),
850             expect![[r#"
851                 dep::fmt (t)
852                 dep::Fmt (t)
853                 dep::Fmt (v)
854                 dep::Fmt (m)
855                 dep::fmt::Display (t)
856                 dep::fmt::Display::fmt (f)
857             "#]],
858         );
859
860         check_search(
861             ra_fixture,
862             "main",
863             Query::new("fmt".to_string()).name_only(),
864             expect![[r#"
865                 dep::fmt (t)
866                 dep::Fmt (t)
867                 dep::Fmt (v)
868                 dep::Fmt (m)
869                 dep::fmt::Display::fmt (f)
870             "#]],
871         );
872     }
873
874     #[test]
875     fn search_casing() {
876         let ra_fixture = r#"
877             //- /main.rs crate:main deps:dep
878             //- /dep.rs crate:dep
879
880             pub struct fmt;
881             pub struct FMT;
882         "#;
883
884         check_search(
885             ra_fixture,
886             "main",
887             Query::new("FMT".to_string()),
888             expect![[r#"
889                 dep::fmt (t)
890                 dep::fmt (v)
891                 dep::FMT (t)
892                 dep::FMT (v)
893             "#]],
894         );
895
896         check_search(
897             ra_fixture,
898             "main",
899             Query::new("FMT".to_string()).case_sensitive(),
900             expect![[r#"
901                 dep::FMT (t)
902                 dep::FMT (v)
903             "#]],
904         );
905     }
906
907     #[test]
908     fn search_limit() {
909         check_search(
910             r#"
911         //- /main.rs crate:main deps:dep
912         //- /dep.rs crate:dep
913         pub mod fmt {
914             pub trait Display {
915                 fn fmt();
916             }
917         }
918         #[macro_export]
919         macro_rules! Fmt {
920             () => {};
921         }
922         pub struct Fmt;
923
924         pub fn format() {}
925         pub fn no() {}
926     "#,
927             "main",
928             Query::new("".to_string()).limit(2),
929             expect![[r#"
930                 dep::fmt (t)
931                 dep::Fmt (t)
932             "#]],
933         );
934     }
935
936     #[test]
937     fn search_exclusions() {
938         let ra_fixture = r#"
939             //- /main.rs crate:main deps:dep
940             //- /dep.rs crate:dep
941
942             pub struct fmt;
943             pub struct FMT;
944         "#;
945
946         check_search(
947             ra_fixture,
948             "main",
949             Query::new("FMT".to_string()),
950             expect![[r#"
951                 dep::fmt (t)
952                 dep::fmt (v)
953                 dep::FMT (t)
954                 dep::FMT (v)
955             "#]],
956         );
957
958         check_search(
959             ra_fixture,
960             "main",
961             Query::new("FMT".to_string()).exclude_import_kind(ImportKind::Adt),
962             expect![[r#""#]],
963         );
964     }
965 }