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