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