]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/import_map.rs
Add docs and optimisations
[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: &str) -> Self {
266         Self {
267             query: query.to_string(),
268             lowercased: query.to_lowercase(),
269             name_only: false,
270             search_mode: SearchMode::Contains,
271             case_sensitive: false,
272             limit: usize::max_value(),
273             exclude_import_kinds: FxHashSet::default(),
274         }
275     }
276
277     /// Matches entries' names only, ignoring the rest of
278     /// the qualifier.
279     /// Example: for `std::marker::PhantomData`, the name is `PhantomData`.
280     pub fn name_only(self) -> Self {
281         Self { name_only: true, ..self }
282     }
283
284     /// Specifies the way to search for the entries using the query.
285     pub fn search_mode(self, search_mode: SearchMode) -> Self {
286         Self { search_mode, ..self }
287     }
288
289     /// Limits the returned number of items to `limit`.
290     pub fn limit(self, limit: usize) -> Self {
291         Self { limit, ..self }
292     }
293
294     /// Respect casing of the query string when matching.
295     pub fn case_sensitive(self) -> Self {
296         Self { case_sensitive: true, ..self }
297     }
298
299     /// Do not include imports of the specified kind in the search results.
300     pub fn exclude_import_kind(mut self, import_kind: ImportKind) -> Self {
301         self.exclude_import_kinds.insert(import_kind);
302         self
303     }
304 }
305
306 fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool {
307     let mut input = if query.name_only {
308         input_path.segments.last().unwrap().to_string()
309     } else {
310         input_path.to_string()
311     };
312     if enforce_lowercase || !query.case_sensitive {
313         input.make_ascii_lowercase();
314     }
315
316     let query_string =
317         if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased };
318
319     match query.search_mode {
320         SearchMode::Equals => &input == query_string,
321         SearchMode::Contains => input.contains(query_string),
322         SearchMode::Fuzzy => {
323             let mut unchecked_query_chars = query_string.chars();
324             let mut mismatching_query_char = unchecked_query_chars.next();
325
326             for input_char in input.chars() {
327                 match mismatching_query_char {
328                     None => return true,
329                     Some(matching_query_char) if matching_query_char == input_char => {
330                         mismatching_query_char = unchecked_query_chars.next();
331                     }
332                     _ => (),
333                 }
334             }
335             mismatching_query_char.is_none()
336         }
337     }
338 }
339
340 /// Searches dependencies of `krate` for an importable path matching `query`.
341 ///
342 /// This returns a list of items that could be imported from dependencies of `krate`.
343 pub fn search_dependencies<'a>(
344     db: &'a dyn DefDatabase,
345     krate: CrateId,
346     query: Query,
347 ) -> Vec<ItemInNs> {
348     let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query));
349
350     let graph = db.crate_graph();
351     let import_maps: Vec<_> =
352         graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
353
354     let automaton = fst::automaton::Subsequence::new(&query.lowercased);
355
356     let mut op = fst::map::OpBuilder::new();
357     for map in &import_maps {
358         op = op.add(map.fst.search(&automaton));
359     }
360
361     let mut stream = op.union();
362     let mut res = Vec::new();
363     while let Some((_, indexed_values)) = stream.next() {
364         for indexed_value in indexed_values {
365             let import_map = &import_maps[indexed_value.index];
366             let importables = &import_map.importables[indexed_value.value as usize..];
367
368             // Path shared by the importable items in this group.
369             let common_importables_path = &import_map.map[&importables[0]].path;
370             if !contains_query(&query, common_importables_path, true) {
371                 continue;
372             }
373
374             let common_importables_path_fst = fst_path(common_importables_path);
375             // Add the items from this `ModPath` group. Those are all subsequent items in
376             // `importables` whose paths match `path`.
377             let iter = importables
378                 .iter()
379                 .copied()
380                 .take_while(|item| {
381                     common_importables_path_fst == fst_path(&import_map.map[item].path)
382                 })
383                 .filter(|&item| match item_import_kind(item) {
384                     Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
385                     None => true,
386                 })
387                 .filter(|item| {
388                     !query.case_sensitive // we've already checked the common importables path case-insensitively
389                         || contains_query(&query, &import_map.map[item].path, false)
390                 });
391             res.extend(iter);
392
393             if res.len() >= query.limit {
394                 res.truncate(query.limit);
395                 return res;
396             }
397         }
398     }
399
400     // Add all exported associated items whose names match the query (exactly).
401     for map in &import_maps {
402         if let Some(v) = map.assoc_map.get(&*query.query) {
403             res.extend(v.iter().map(|&assoc| {
404                 ItemInNs::Types(match assoc {
405                     AssocItemId::FunctionId(it) => it.into(),
406                     AssocItemId::ConstId(it) => it.into(),
407                     AssocItemId::TypeAliasId(it) => it.into(),
408                 })
409             }));
410         }
411     }
412
413     res
414 }
415
416 fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
417     Some(match item.as_module_def_id()? {
418         ModuleDefId::ModuleId(_) => ImportKind::Module,
419         ModuleDefId::FunctionId(_) => ImportKind::Function,
420         ModuleDefId::AdtId(_) => ImportKind::Adt,
421         ModuleDefId::EnumVariantId(_) => ImportKind::EnumVariant,
422         ModuleDefId::ConstId(_) => ImportKind::Const,
423         ModuleDefId::StaticId(_) => ImportKind::Static,
424         ModuleDefId::TraitId(_) => ImportKind::Trait,
425         ModuleDefId::TypeAliasId(_) => ImportKind::TypeAlias,
426         ModuleDefId::BuiltinType(_) => ImportKind::BuiltinType,
427     })
428 }
429
430 #[cfg(test)]
431 mod tests {
432     use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
433     use expect_test::{expect, Expect};
434
435     use crate::{test_db::TestDB, AssocContainerId, Lookup};
436
437     use super::*;
438
439     fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) {
440         let db = TestDB::with_files(ra_fixture);
441         let crate_graph = db.crate_graph();
442         let krate = crate_graph
443             .iter()
444             .find(|krate| {
445                 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
446                     == Some(crate_name.to_string())
447             })
448             .unwrap();
449
450         let actual = search_dependencies(db.upcast(), krate, query)
451             .into_iter()
452             .filter_map(|item| {
453                 let mark = match item {
454                     ItemInNs::Types(_) => "t",
455                     ItemInNs::Values(_) => "v",
456                     ItemInNs::Macros(_) => "m",
457                 };
458                 let item = assoc_to_trait(&db, item);
459                 item.krate(db.upcast()).map(|krate| {
460                     let map = db.import_map(krate);
461                     let path = map.path_of(item).unwrap();
462                     format!(
463                         "{}::{} ({})\n",
464                         crate_graph[krate].display_name.as_ref().unwrap(),
465                         path,
466                         mark
467                     )
468                 })
469             })
470             .collect::<String>();
471         expect.assert_eq(&actual)
472     }
473
474     fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs {
475         let assoc: AssocItemId = match item {
476             ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
477                 ModuleDefId::TypeAliasId(it) => it.into(),
478                 ModuleDefId::FunctionId(it) => it.into(),
479                 ModuleDefId::ConstId(it) => it.into(),
480                 _ => return item,
481             },
482             _ => return item,
483         };
484
485         let container = match assoc {
486             AssocItemId::FunctionId(it) => it.lookup(db).container,
487             AssocItemId::ConstId(it) => it.lookup(db).container,
488             AssocItemId::TypeAliasId(it) => it.lookup(db).container,
489         };
490
491         match container {
492             AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()),
493             _ => item,
494         }
495     }
496
497     fn check(ra_fixture: &str, expect: Expect) {
498         let db = TestDB::with_files(ra_fixture);
499         let crate_graph = db.crate_graph();
500
501         let actual = crate_graph
502             .iter()
503             .filter_map(|krate| {
504                 let cdata = &crate_graph[krate];
505                 let name = cdata.display_name.as_ref()?;
506
507                 let map = db.import_map(krate);
508
509                 Some(format!("{}:\n{:?}\n", name, map))
510             })
511             .collect::<String>();
512
513         expect.assert_eq(&actual)
514     }
515
516     #[test]
517     fn smoke() {
518         check(
519             r"
520             //- /main.rs crate:main deps:lib
521
522             mod private {
523                 pub use lib::Pub;
524                 pub struct InPrivateModule;
525             }
526
527             pub mod publ1 {
528                 use lib::Pub;
529             }
530
531             pub mod real_pub {
532                 pub use lib::Pub;
533             }
534             pub mod real_pu2 { // same path length as above
535                 pub use lib::Pub;
536             }
537
538             //- /lib.rs crate:lib
539             pub struct Pub {}
540             pub struct Pub2; // t + v
541             struct Priv;
542         ",
543             expect![[r#"
544                 main:
545                 - publ1 (t)
546                 - real_pu2 (t)
547                 - real_pub (t)
548                 - real_pub::Pub (t)
549                 lib:
550                 - Pub (t)
551                 - Pub2 (t)
552                 - Pub2 (v)
553             "#]],
554         );
555     }
556
557     #[test]
558     fn prefers_shortest_path() {
559         check(
560             r"
561             //- /main.rs crate:main
562
563             pub mod sub {
564                 pub mod subsub {
565                     pub struct Def {}
566                 }
567
568                 pub use super::sub::subsub::Def;
569             }
570         ",
571             expect![[r#"
572                 main:
573                 - sub (t)
574                 - sub::Def (t)
575                 - sub::subsub (t)
576             "#]],
577         );
578     }
579
580     #[test]
581     fn type_reexport_cross_crate() {
582         // Reexports need to be visible from a crate, even if the original crate exports the item
583         // at a shorter path.
584         check(
585             r"
586             //- /main.rs crate:main deps:lib
587             pub mod m {
588                 pub use lib::S;
589             }
590             //- /lib.rs crate:lib
591             pub struct S;
592         ",
593             expect![[r#"
594                 main:
595                 - m (t)
596                 - m::S (t)
597                 - m::S (v)
598                 lib:
599                 - S (t)
600                 - S (v)
601             "#]],
602         );
603     }
604
605     #[test]
606     fn macro_reexport() {
607         check(
608             r"
609             //- /main.rs crate:main deps:lib
610             pub mod m {
611                 pub use lib::pub_macro;
612             }
613             //- /lib.rs crate:lib
614             #[macro_export]
615             macro_rules! pub_macro {
616                 () => {};
617             }
618         ",
619             expect![[r#"
620                 main:
621                 - m (t)
622                 - m::pub_macro (m)
623                 lib:
624                 - pub_macro (m)
625             "#]],
626         );
627     }
628
629     #[test]
630     fn module_reexport() {
631         // Reexporting modules from a dependency adds all contents to the import map.
632         check(
633             r"
634             //- /main.rs crate:main deps:lib
635             pub use lib::module as reexported_module;
636             //- /lib.rs crate:lib
637             pub mod module {
638                 pub struct S;
639             }
640         ",
641             expect![[r#"
642                 main:
643                 - reexported_module (t)
644                 - reexported_module::S (t)
645                 - reexported_module::S (v)
646                 lib:
647                 - module (t)
648                 - module::S (t)
649                 - module::S (v)
650             "#]],
651         );
652     }
653
654     #[test]
655     fn cyclic_module_reexport() {
656         // A cyclic reexport does not hang.
657         check(
658             r"
659             //- /lib.rs crate:lib
660             pub mod module {
661                 pub struct S;
662                 pub use super::sub::*;
663             }
664
665             pub mod sub {
666                 pub use super::module;
667             }
668         ",
669             expect![[r#"
670                 lib:
671                 - module (t)
672                 - module::S (t)
673                 - module::S (v)
674                 - sub (t)
675             "#]],
676         );
677     }
678
679     #[test]
680     fn private_macro() {
681         check(
682             r"
683             //- /lib.rs crate:lib
684             macro_rules! private_macro {
685                 () => {};
686             }
687         ",
688             expect![[r#"
689                 lib:
690
691             "#]],
692         );
693     }
694
695     #[test]
696     fn namespacing() {
697         check(
698             r"
699             //- /lib.rs crate:lib
700             pub struct Thing;     // t + v
701             #[macro_export]
702             macro_rules! Thing {  // m
703                 () => {};
704             }
705         ",
706             expect![[r#"
707                 lib:
708                 - Thing (m)
709                 - Thing (t)
710                 - Thing (v)
711             "#]],
712         );
713
714         check(
715             r"
716             //- /lib.rs crate:lib
717             pub mod Thing {}      // t
718             #[macro_export]
719             macro_rules! Thing {  // m
720                 () => {};
721             }
722         ",
723             expect![[r#"
724                 lib:
725                 - Thing (m)
726                 - Thing (t)
727             "#]],
728         );
729     }
730
731     #[test]
732     fn search() {
733         let ra_fixture = r#"
734             //- /main.rs crate:main deps:dep
735             //- /dep.rs crate:dep deps:tdep
736             use tdep::fmt as fmt_dep;
737             pub mod fmt {
738                 pub trait Display {
739                     fn fmt();
740                 }
741             }
742             #[macro_export]
743             macro_rules! Fmt {
744                 () => {};
745             }
746             pub struct Fmt;
747
748             pub fn format() {}
749             pub fn no() {}
750
751             //- /tdep.rs crate:tdep
752             pub mod fmt {
753                 pub struct NotImportableFromMain;
754             }
755         "#;
756
757         check_search(
758             ra_fixture,
759             "main",
760             Query::new("fmt").search_mode(SearchMode::Fuzzy),
761             expect![[r#"
762                 dep::fmt (t)
763                 dep::Fmt (t)
764                 dep::Fmt (v)
765                 dep::Fmt (m)
766                 dep::fmt::Display (t)
767                 dep::format (v)
768                 dep::fmt::Display (t)
769             "#]],
770         );
771
772         check_search(
773             ra_fixture,
774             "main",
775             Query::new("fmt").name_only().search_mode(SearchMode::Equals),
776             expect![[r#"
777                 dep::fmt (t)
778                 dep::Fmt (t)
779                 dep::Fmt (v)
780                 dep::Fmt (m)
781                 dep::fmt::Display (t)
782             "#]],
783         );
784     }
785
786     #[test]
787     fn search_casing() {
788         let ra_fixture = r#"
789             //- /main.rs crate:main deps:dep
790             //- /dep.rs crate:dep
791
792             pub struct fmt;
793             pub struct FMT;
794         "#;
795
796         check_search(
797             ra_fixture,
798             "main",
799             Query::new("FMT"),
800             expect![[r#"
801                 dep::fmt (t)
802                 dep::fmt (v)
803                 dep::FMT (t)
804                 dep::FMT (v)
805             "#]],
806         );
807
808         check_search(
809             ra_fixture,
810             "main",
811             Query::new("FMT").case_sensitive(),
812             expect![[r#"
813                 dep::FMT (t)
814                 dep::FMT (v)
815             "#]],
816         );
817     }
818
819     #[test]
820     fn search_limit() {
821         check_search(
822             r#"
823         //- /main.rs crate:main deps:dep
824         //- /dep.rs crate:dep
825         pub mod fmt {
826             pub trait Display {
827                 fn fmt();
828             }
829         }
830         #[macro_export]
831         macro_rules! Fmt {
832             () => {};
833         }
834         pub struct Fmt;
835
836         pub fn format() {}
837         pub fn no() {}
838     "#,
839             "main",
840             Query::new("").limit(2),
841             expect![[r#"
842                 dep::fmt (t)
843                 dep::Fmt (t)
844             "#]],
845         );
846     }
847
848     #[test]
849     fn search_exclusions() {
850         let ra_fixture = r#"
851             //- /main.rs crate:main deps:dep
852             //- /dep.rs crate:dep
853
854             pub struct fmt;
855             pub struct FMT;
856         "#;
857
858         check_search(
859             ra_fixture,
860             "main",
861             Query::new("FMT"),
862             expect![[r#"
863                 dep::fmt (t)
864                 dep::fmt (v)
865                 dep::FMT (t)
866                 dep::FMT (v)
867             "#]],
868         );
869
870         check_search(
871             ra_fixture,
872             "main",
873             Query::new("FMT").exclude_import_kind(ImportKind::Adt),
874             expect![[r#""#]],
875         );
876     }
877 }