]> git.lizzy.rs Git - rust.git/blob - crates/hir_def/src/find_path.rs
parameters.split_last()
[rust.git] / crates / hir_def / src / find_path.rs
1 //! An algorithm to find a path to refer to a certain item.
2
3 use std::iter;
4
5 use hir_expand::name::{known, AsName, Name};
6 use rustc_hash::FxHashSet;
7
8 use crate::{
9     db::DefDatabase,
10     item_scope::ItemInNs,
11     nameres::DefMap,
12     path::{ModPath, PathKind},
13     visibility::Visibility,
14     ModuleDefId, ModuleId,
15 };
16
17 /// Find a path that can be used to refer to a certain item. This can depend on
18 /// *from where* you're referring to the item, hence the `from` parameter.
19 pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
20     let _p = profile::span("find_path");
21     let mut visited_modules = FxHashSet::default();
22     find_path_inner(db, item, from, MAX_PATH_LEN, None, &mut visited_modules)
23 }
24
25 pub fn find_path_prefixed(
26     db: &dyn DefDatabase,
27     item: ItemInNs,
28     from: ModuleId,
29     prefix_kind: PrefixKind,
30 ) -> Option<ModPath> {
31     let _p = profile::span("find_path_prefixed");
32     let mut visited_modules = FxHashSet::default();
33     find_path_inner(db, item, from, MAX_PATH_LEN, Some(prefix_kind), &mut visited_modules)
34 }
35
36 const MAX_PATH_LEN: usize = 15;
37
38 trait ModPathExt {
39     fn starts_with_std(&self) -> bool;
40     fn can_start_with_std(&self) -> bool;
41 }
42
43 impl ModPathExt for ModPath {
44     fn starts_with_std(&self) -> bool {
45         self.segments().first() == Some(&known::std)
46     }
47
48     // When std library is present, paths starting with `std::`
49     // should be preferred over paths starting with `core::` and `alloc::`
50     fn can_start_with_std(&self) -> bool {
51         let first_segment = self.segments().first();
52         first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
53     }
54 }
55
56 fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
57     if item == ItemInNs::Types(from.into()) {
58         // - if the item is the module we're in, use `self`
59         Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
60     } else if let Some(parent_id) = def_map[from.local_id].parent {
61         // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
62         let parent_id = def_map.module_id(parent_id);
63         if item == ItemInNs::Types(ModuleDefId::ModuleId(parent_id)) {
64             Some(ModPath::from_segments(PathKind::Super(1), Vec::new()))
65         } else {
66             None
67         }
68     } else {
69         None
70     }
71 }
72
73 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
74 pub enum PrefixKind {
75     /// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
76     /// This is the same as plain, just that paths will start with `self` iprepended f the path
77     /// starts with an identifier that is not a crate.
78     BySelf,
79     /// Causes paths to ignore imports in the local module.
80     Plain,
81     /// Causes paths to start with `crate` where applicable, effectively forcing paths to be absolute.
82     ByCrate,
83 }
84
85 impl PrefixKind {
86     #[inline]
87     fn prefix(self) -> PathKind {
88         match self {
89             PrefixKind::BySelf => PathKind::Super(0),
90             PrefixKind::Plain => PathKind::Plain,
91             PrefixKind::ByCrate => PathKind::Crate,
92         }
93     }
94
95     #[inline]
96     fn is_absolute(&self) -> bool {
97         self == &PrefixKind::ByCrate
98     }
99 }
100
101 fn find_path_inner(
102     db: &dyn DefDatabase,
103     item: ItemInNs,
104     from: ModuleId,
105     max_len: usize,
106     mut prefixed: Option<PrefixKind>,
107     visited_modules: &mut FxHashSet<ModuleId>,
108 ) -> Option<ModPath> {
109     if max_len == 0 {
110         return None;
111     }
112
113     // Base cases:
114
115     // - if the item is already in scope, return the name under which it is
116     let def_map = from.def_map(db);
117     let scope_name = def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
118         def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone())
119     });
120     if prefixed.is_none() && scope_name.is_some() {
121         return scope_name
122             .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name]));
123     }
124
125     // - if the item is the crate root, return `crate`
126     let root = def_map.crate_root(db);
127     if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) {
128         return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
129     }
130
131     if prefixed.filter(PrefixKind::is_absolute).is_none() {
132         if let modpath @ Some(_) = check_self_super(&def_map, item, from) {
133             return modpath;
134         }
135     }
136
137     // - if the item is the crate root of a dependency crate, return the name from the extern prelude
138     let root_def_map = root.def_map(db);
139     for (name, def_id) in root_def_map.extern_prelude() {
140         if item == ItemInNs::Types(*def_id) {
141             let name = scope_name.unwrap_or_else(|| name.clone());
142
143             let name_already_occupied_in_type_ns = def_map
144                 .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
145                     def_map[local_id].scope.get(&name).take_types().filter(|&id| id != *def_id)
146                 })
147                 .is_some();
148             return Some(ModPath::from_segments(
149                 if name_already_occupied_in_type_ns {
150                     cov_mark::hit!(ambiguous_crate_start);
151                     PathKind::Abs
152                 } else {
153                     PathKind::Plain
154                 },
155                 vec![name],
156             ));
157         }
158     }
159
160     // - if the item is in the prelude, return the name from there
161     if let Some(prelude_module) = root_def_map.prelude() {
162         // Preludes in block DefMaps are ignored, only the crate DefMap is searched
163         let prelude_def_map = prelude_module.def_map(db);
164         let prelude_scope: &crate::item_scope::ItemScope =
165             &prelude_def_map[prelude_module.local_id].scope;
166         if let Some((name, vis)) = prelude_scope.name_of(item) {
167             if vis.is_visible_from(db, from) {
168                 return Some(ModPath::from_segments(PathKind::Plain, vec![name.clone()]));
169             }
170         }
171     }
172
173     // - if the item is a builtin, it's in scope
174     if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
175         return Some(ModPath::from_segments(PathKind::Plain, vec![builtin.as_name()]));
176     }
177
178     // Recursive case:
179     // - if the item is an enum variant, refer to it via the enum
180     if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
181         if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) {
182             let data = db.enum_data(variant.parent);
183             path.push_segment(data.variants[variant.local_id].name.clone());
184             return Some(path);
185         }
186         // If this doesn't work, it seems we have no way of referring to the
187         // enum; that's very weird, but there might still be a reexport of the
188         // variant somewhere
189     }
190
191     // - otherwise, look for modules containing (reexporting) it and import it from one of those
192
193     let crate_root = def_map.crate_root(db);
194     let crate_attrs = db.attrs(crate_root.into());
195     let prefer_no_std = crate_attrs.by_key("no_std").exists();
196     let mut best_path = None;
197     let mut best_path_len = max_len;
198
199     if item.krate(db) == Some(from.krate) {
200         // Item was defined in the same crate that wants to import it. It cannot be found in any
201         // dependency in this case.
202         for (module_id, name) in find_local_import_locations(db, item, from) {
203             if !visited_modules.insert(module_id) {
204                 cov_mark::hit!(recursive_imports);
205                 continue;
206             }
207             if let Some(mut path) = find_path_inner(
208                 db,
209                 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
210                 from,
211                 best_path_len - 1,
212                 prefixed,
213                 visited_modules,
214             ) {
215                 path.push_segment(name);
216
217                 let new_path = match best_path {
218                     Some(best_path) => select_best_path(best_path, path, prefer_no_std),
219                     None => path,
220                 };
221                 best_path_len = new_path.len();
222                 best_path = Some(new_path);
223             }
224         }
225     } else {
226         // Item was defined in some upstream crate. This means that it must be exported from one,
227         // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
228         // that wants to import it here, but we always prefer to use the external path here.
229
230         let crate_graph = db.crate_graph();
231         let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
232             let import_map = db.import_map(dep.crate_id);
233             import_map.import_info_for(item).and_then(|info| {
234                 // Determine best path for containing module and append last segment from `info`.
235                 let mut path = find_path_inner(
236                     db,
237                     ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
238                     from,
239                     best_path_len - 1,
240                     prefixed,
241                     visited_modules,
242                 )?;
243                 cov_mark::hit!(partially_imported);
244                 path.push_segment(info.path.segments.last().unwrap().clone());
245                 Some(path)
246             })
247         });
248
249         for path in extern_paths {
250             let new_path = match best_path {
251                 Some(best_path) => select_best_path(best_path, path, prefer_no_std),
252                 None => path,
253             };
254             best_path = Some(new_path);
255         }
256     }
257
258     // If the item is declared inside a block expression, don't use a prefix, as we don't handle
259     // that correctly (FIXME).
260     if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) {
261         if item_module.def_map(db).block_id().is_some() && prefixed.is_some() {
262             cov_mark::hit!(prefixed_in_block_expression);
263             prefixed = Some(PrefixKind::Plain);
264         }
265     }
266
267     match prefixed.map(PrefixKind::prefix) {
268         Some(prefix) => best_path.or_else(|| {
269             scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
270         }),
271         None => best_path,
272     }
273 }
274
275 fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
276     if old_path.starts_with_std() && new_path.can_start_with_std() {
277         if prefer_no_std {
278             cov_mark::hit!(prefer_no_std_paths);
279             new_path
280         } else {
281             cov_mark::hit!(prefer_std_paths);
282             old_path
283         }
284     } else if new_path.starts_with_std() && old_path.can_start_with_std() {
285         if prefer_no_std {
286             cov_mark::hit!(prefer_no_std_paths);
287             old_path
288         } else {
289             cov_mark::hit!(prefer_std_paths);
290             new_path
291         }
292     } else if new_path.len() < old_path.len() {
293         new_path
294     } else {
295         old_path
296     }
297 }
298
299 /// Finds locations in `from.krate` from which `item` can be imported by `from`.
300 fn find_local_import_locations(
301     db: &dyn DefDatabase,
302     item: ItemInNs,
303     from: ModuleId,
304 ) -> Vec<(ModuleId, Name)> {
305     let _p = profile::span("find_local_import_locations");
306
307     // `from` can import anything below `from` with visibility of at least `from`, and anything
308     // above `from` with any visibility. That means we do not need to descend into private siblings
309     // of `from` (and similar).
310
311     let def_map = from.def_map(db);
312
313     // Compute the initial worklist. We start with all direct child modules of `from` as well as all
314     // of its (recursive) parent modules.
315     let data = &def_map[from.local_id];
316     let mut worklist =
317         data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>();
318     // FIXME: do we need to traverse out of block expressions here?
319     for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) {
320         worklist.push(ancestor);
321     }
322
323     let def_map = def_map.crate_root(db).def_map(db);
324
325     let mut seen: FxHashSet<_> = FxHashSet::default();
326
327     let mut locations = Vec::new();
328     while let Some(module) = worklist.pop() {
329         if !seen.insert(module) {
330             continue; // already processed this module
331         }
332
333         let ext_def_map;
334         let data = if module.krate == from.krate {
335             if module.block.is_some() {
336                 // Re-query the block's DefMap
337                 ext_def_map = module.def_map(db);
338                 &ext_def_map[module.local_id]
339             } else {
340                 // Reuse the root DefMap
341                 &def_map[module.local_id]
342             }
343         } else {
344             // The crate might reexport a module defined in another crate.
345             ext_def_map = module.def_map(db);
346             &ext_def_map[module.local_id]
347         };
348
349         if let Some((name, vis)) = data.scope.name_of(item) {
350             if vis.is_visible_from(db, from) {
351                 let is_private = match vis {
352                     Visibility::Module(private_to) => private_to.local_id == module.local_id,
353                     Visibility::Public => false,
354                 };
355                 let is_original_def = match item.as_module_def_id() {
356                     Some(module_def_id) => data.scope.declarations().any(|it| it == module_def_id),
357                     None => false,
358                 };
359
360                 // Ignore private imports. these could be used if we are
361                 // in a submodule of this module, but that's usually not
362                 // what the user wants; and if this module can import
363                 // the item and we're a submodule of it, so can we.
364                 // Also this keeps the cached data smaller.
365                 if !is_private || is_original_def {
366                     locations.push((module, name.clone()));
367                 }
368             }
369         }
370
371         // Descend into all modules visible from `from`.
372         for (_, per_ns) in data.scope.entries() {
373             if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
374                 if vis.is_visible_from(db, from) {
375                     worklist.push(module);
376                 }
377             }
378         }
379     }
380
381     locations
382 }
383
384 #[cfg(test)]
385 mod tests {
386     use base_db::fixture::WithFixture;
387     use hir_expand::hygiene::Hygiene;
388     use syntax::ast::AstNode;
389
390     use crate::test_db::TestDB;
391
392     use super::*;
393
394     /// `code` needs to contain a cursor marker; checks that `find_path` for the
395     /// item the `path` refers to returns that same path when called from the
396     /// module the cursor is in.
397     fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) {
398         let (db, pos) = TestDB::with_position(ra_fixture);
399         let module = db.module_at_position(pos);
400         let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
401         let ast_path =
402             parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
403         let mod_path = ModPath::from_src(&db, ast_path, &Hygiene::new_unhygienic()).unwrap();
404
405         let def_map = module.def_map(&db);
406         let resolved = def_map
407             .resolve_path(
408                 &db,
409                 module.local_id,
410                 &mod_path,
411                 crate::item_scope::BuiltinShadowMode::Module,
412             )
413             .0
414             .take_types()
415             .unwrap();
416
417         let mut visited_modules = FxHashSet::default();
418         let found_path = find_path_inner(
419             &db,
420             ItemInNs::Types(resolved),
421             module,
422             MAX_PATH_LEN,
423             prefix_kind,
424             &mut visited_modules,
425         );
426         assert_eq!(found_path, Some(mod_path), "{:?}", prefix_kind);
427     }
428
429     fn check_found_path(
430         ra_fixture: &str,
431         unprefixed: &str,
432         prefixed: &str,
433         absolute: &str,
434         self_prefixed: &str,
435     ) {
436         check_found_path_(ra_fixture, unprefixed, None);
437         check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain));
438         check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate));
439         check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf));
440     }
441
442     #[test]
443     fn same_module() {
444         check_found_path(
445             r#"
446 struct S;
447 $0
448         "#,
449             "S",
450             "S",
451             "crate::S",
452             "self::S",
453         );
454     }
455
456     #[test]
457     fn enum_variant() {
458         check_found_path(
459             r#"
460 enum E { A }
461 $0
462         "#,
463             "E::A",
464             "E::A",
465             "E::A",
466             "E::A",
467         );
468     }
469
470     #[test]
471     fn sub_module() {
472         check_found_path(
473             r#"
474 mod foo {
475     pub struct S;
476 }
477 $0
478         "#,
479             "foo::S",
480             "foo::S",
481             "crate::foo::S",
482             "self::foo::S",
483         );
484     }
485
486     #[test]
487     fn super_module() {
488         check_found_path(
489             r#"
490 //- /main.rs
491 mod foo;
492 //- /foo.rs
493 mod bar;
494 struct S;
495 //- /foo/bar.rs
496 $0
497         "#,
498             "super::S",
499             "super::S",
500             "crate::foo::S",
501             "super::S",
502         );
503     }
504
505     #[test]
506     fn self_module() {
507         check_found_path(
508             r#"
509 //- /main.rs
510 mod foo;
511 //- /foo.rs
512 $0
513         "#,
514             "self",
515             "self",
516             "crate::foo",
517             "self",
518         );
519     }
520
521     #[test]
522     fn crate_root() {
523         check_found_path(
524             r#"
525 //- /main.rs
526 mod foo;
527 //- /foo.rs
528 $0
529         "#,
530             "crate",
531             "crate",
532             "crate",
533             "crate",
534         );
535     }
536
537     #[test]
538     fn same_crate() {
539         check_found_path(
540             r#"
541 //- /main.rs
542 mod foo;
543 struct S;
544 //- /foo.rs
545 $0
546         "#,
547             "crate::S",
548             "crate::S",
549             "crate::S",
550             "crate::S",
551         );
552     }
553
554     #[test]
555     fn different_crate() {
556         check_found_path(
557             r#"
558 //- /main.rs crate:main deps:std
559 $0
560 //- /std.rs crate:std
561 pub struct S;
562         "#,
563             "std::S",
564             "std::S",
565             "std::S",
566             "std::S",
567         );
568     }
569
570     #[test]
571     fn different_crate_renamed() {
572         check_found_path(
573             r#"
574 //- /main.rs crate:main deps:std
575 extern crate std as std_renamed;
576 $0
577 //- /std.rs crate:std
578 pub struct S;
579         "#,
580             "std_renamed::S",
581             "std_renamed::S",
582             "std_renamed::S",
583             "std_renamed::S",
584         );
585     }
586
587     #[test]
588     fn partially_imported() {
589         cov_mark::check!(partially_imported);
590         // Tests that short paths are used even for external items, when parts of the path are
591         // already in scope.
592         check_found_path(
593             r#"
594 //- /main.rs crate:main deps:syntax
595
596 use syntax::ast;
597 $0
598
599 //- /lib.rs crate:syntax
600 pub mod ast {
601     pub enum ModuleItem {
602         A, B, C,
603     }
604 }
605         "#,
606             "ast::ModuleItem",
607             "syntax::ast::ModuleItem",
608             "syntax::ast::ModuleItem",
609             "syntax::ast::ModuleItem",
610         );
611
612         check_found_path(
613             r#"
614 //- /main.rs crate:main deps:syntax
615 $0
616
617 //- /lib.rs crate:syntax
618 pub mod ast {
619     pub enum ModuleItem {
620         A, B, C,
621     }
622 }
623         "#,
624             "syntax::ast::ModuleItem",
625             "syntax::ast::ModuleItem",
626             "syntax::ast::ModuleItem",
627             "syntax::ast::ModuleItem",
628         );
629     }
630
631     #[test]
632     fn same_crate_reexport() {
633         check_found_path(
634             r#"
635 mod bar {
636     mod foo { pub(super) struct S; }
637     pub(crate) use foo::*;
638 }
639 $0
640         "#,
641             "bar::S",
642             "bar::S",
643             "crate::bar::S",
644             "self::bar::S",
645         );
646     }
647
648     #[test]
649     fn same_crate_reexport_rename() {
650         check_found_path(
651             r#"
652 mod bar {
653     mod foo { pub(super) struct S; }
654     pub(crate) use foo::S as U;
655 }
656 $0
657         "#,
658             "bar::U",
659             "bar::U",
660             "crate::bar::U",
661             "self::bar::U",
662         );
663     }
664
665     #[test]
666     fn different_crate_reexport() {
667         check_found_path(
668             r#"
669 //- /main.rs crate:main deps:std
670 $0
671 //- /std.rs crate:std deps:core
672 pub use core::S;
673 //- /core.rs crate:core
674 pub struct S;
675         "#,
676             "std::S",
677             "std::S",
678             "std::S",
679             "std::S",
680         );
681     }
682
683     #[test]
684     fn prelude() {
685         check_found_path(
686             r#"
687 //- /main.rs crate:main deps:std
688 $0
689 //- /std.rs crate:std
690 pub mod prelude {
691     pub mod rust_2018 {
692         pub struct S;
693     }
694 }
695         "#,
696             "S",
697             "S",
698             "S",
699             "S",
700         );
701     }
702
703     #[test]
704     fn enum_variant_from_prelude() {
705         let code = r#"
706 //- /main.rs crate:main deps:std
707 $0
708 //- /std.rs crate:std
709 pub mod prelude {
710     pub mod rust_2018 {
711         pub enum Option<T> { Some(T), None }
712         pub use Option::*;
713     }
714 }
715         "#;
716         check_found_path(code, "None", "None", "None", "None");
717         check_found_path(code, "Some", "Some", "Some", "Some");
718     }
719
720     #[test]
721     fn shortest_path() {
722         check_found_path(
723             r#"
724 //- /main.rs
725 pub mod foo;
726 pub mod baz;
727 struct S;
728 $0
729 //- /foo.rs
730 pub mod bar { pub struct S; }
731 //- /baz.rs
732 pub use crate::foo::bar::S;
733         "#,
734             "baz::S",
735             "baz::S",
736             "crate::baz::S",
737             "self::baz::S",
738         );
739     }
740
741     #[test]
742     fn discount_private_imports() {
743         check_found_path(
744             r#"
745 //- /main.rs
746 mod foo;
747 pub mod bar { pub struct S; }
748 use bar::S;
749 //- /foo.rs
750 $0
751         "#,
752             // crate::S would be shorter, but using private imports seems wrong
753             "crate::bar::S",
754             "crate::bar::S",
755             "crate::bar::S",
756             "crate::bar::S",
757         );
758     }
759
760     #[test]
761     fn import_cycle() {
762         check_found_path(
763             r#"
764 //- /main.rs
765 pub mod foo;
766 pub mod bar;
767 pub mod baz;
768 //- /bar.rs
769 $0
770 //- /foo.rs
771 pub use super::baz;
772 pub struct S;
773 //- /baz.rs
774 pub use super::foo;
775         "#,
776             "crate::foo::S",
777             "crate::foo::S",
778             "crate::foo::S",
779             "crate::foo::S",
780         );
781     }
782
783     #[test]
784     fn prefer_std_paths_over_alloc() {
785         cov_mark::check!(prefer_std_paths);
786         check_found_path(
787             r#"
788 //- /main.rs crate:main deps:alloc,std
789 $0
790
791 //- /std.rs crate:std deps:alloc
792 pub mod sync {
793     pub use alloc::sync::Arc;
794 }
795
796 //- /zzz.rs crate:alloc
797 pub mod sync {
798     pub struct Arc;
799 }
800         "#,
801             "std::sync::Arc",
802             "std::sync::Arc",
803             "std::sync::Arc",
804             "std::sync::Arc",
805         );
806     }
807
808     #[test]
809     fn prefer_core_paths_over_std() {
810         cov_mark::check!(prefer_no_std_paths);
811         check_found_path(
812             r#"
813 //- /main.rs crate:main deps:core,std
814 #![no_std]
815
816 $0
817
818 //- /std.rs crate:std deps:core
819
820 pub mod fmt {
821     pub use core::fmt::Error;
822 }
823
824 //- /zzz.rs crate:core
825
826 pub mod fmt {
827     pub struct Error;
828 }
829         "#,
830             "core::fmt::Error",
831             "core::fmt::Error",
832             "core::fmt::Error",
833             "core::fmt::Error",
834         );
835     }
836
837     #[test]
838     fn prefer_alloc_paths_over_std() {
839         check_found_path(
840             r#"
841 //- /main.rs crate:main deps:alloc,std
842 #![no_std]
843
844 $0
845
846 //- /std.rs crate:std deps:alloc
847
848 pub mod sync {
849     pub use alloc::sync::Arc;
850 }
851
852 //- /zzz.rs crate:alloc
853
854 pub mod sync {
855     pub struct Arc;
856 }
857             "#,
858             "alloc::sync::Arc",
859             "alloc::sync::Arc",
860             "alloc::sync::Arc",
861             "alloc::sync::Arc",
862         );
863     }
864
865     #[test]
866     fn prefer_shorter_paths_if_not_alloc() {
867         check_found_path(
868             r#"
869 //- /main.rs crate:main deps:megaalloc,std
870 $0
871
872 //- /std.rs crate:std deps:megaalloc
873 pub mod sync {
874     pub use megaalloc::sync::Arc;
875 }
876
877 //- /zzz.rs crate:megaalloc
878 pub struct Arc;
879             "#,
880             "megaalloc::Arc",
881             "megaalloc::Arc",
882             "megaalloc::Arc",
883             "megaalloc::Arc",
884         );
885     }
886
887     #[test]
888     fn builtins_are_in_scope() {
889         let code = r#"
890 $0
891
892 pub mod primitive {
893     pub use u8;
894 }
895         "#;
896         check_found_path(code, "u8", "u8", "u8", "u8");
897         check_found_path(code, "u16", "u16", "u16", "u16");
898     }
899
900     #[test]
901     fn inner_items() {
902         check_found_path(
903             r#"
904 fn main() {
905     struct Inner {}
906     $0
907 }
908         "#,
909             "Inner",
910             "Inner",
911             "Inner",
912             "Inner",
913         );
914     }
915
916     #[test]
917     fn inner_items_from_outer_scope() {
918         check_found_path(
919             r#"
920 fn main() {
921     struct Struct {}
922     {
923         $0
924     }
925 }
926         "#,
927             "Struct",
928             "Struct",
929             "Struct",
930             "Struct",
931         );
932     }
933
934     #[test]
935     fn inner_items_from_inner_module() {
936         cov_mark::check!(prefixed_in_block_expression);
937         check_found_path(
938             r#"
939 fn main() {
940     mod module {
941         struct Struct {}
942     }
943     {
944         $0
945     }
946 }
947         "#,
948             "module::Struct",
949             "module::Struct",
950             "module::Struct",
951             "module::Struct",
952         );
953     }
954
955     #[test]
956     fn outer_items_with_inner_items_present() {
957         check_found_path(
958             r#"
959 mod module {
960     pub struct CompleteMe;
961 }
962
963 fn main() {
964     fn inner() {}
965     $0
966 }
967             "#,
968             // FIXME: these could use fewer/better prefixes
969             "module::CompleteMe",
970             "crate::module::CompleteMe",
971             "crate::module::CompleteMe",
972             "crate::module::CompleteMe",
973         )
974     }
975
976     #[test]
977     fn from_inside_module() {
978         // This worked correctly, but the test suite logic was broken.
979         cov_mark::check!(submodule_in_testdb);
980         check_found_path(
981             r#"
982 mod baz {
983     pub struct Foo {}
984 }
985
986 mod bar {
987     fn bar() {
988         $0
989     }
990 }
991             "#,
992             "crate::baz::Foo",
993             "crate::baz::Foo",
994             "crate::baz::Foo",
995             "crate::baz::Foo",
996         )
997     }
998
999     #[test]
1000     fn from_inside_module_with_inner_items() {
1001         check_found_path(
1002             r#"
1003 mod baz {
1004     pub struct Foo {}
1005 }
1006
1007 mod bar {
1008     fn bar() {
1009         fn inner() {}
1010         $0
1011     }
1012 }
1013             "#,
1014             "crate::baz::Foo",
1015             "crate::baz::Foo",
1016             "crate::baz::Foo",
1017             "crate::baz::Foo",
1018         )
1019     }
1020
1021     #[test]
1022     fn recursive_pub_mod_reexport() {
1023         cov_mark::check!(recursive_imports);
1024         check_found_path(
1025             r#"
1026 fn main() {
1027     let _ = 22_i32.as_name$0();
1028 }
1029
1030 pub mod name {
1031     pub trait AsName {
1032         fn as_name(&self) -> String;
1033     }
1034     impl AsName for i32 {
1035         fn as_name(&self) -> String {
1036             format!("Name: {}", self)
1037         }
1038     }
1039     pub use crate::name;
1040 }
1041 "#,
1042             "name::AsName",
1043             "name::AsName",
1044             "crate::name::AsName",
1045             "self::name::AsName",
1046         );
1047     }
1048
1049     #[test]
1050     fn extern_crate() {
1051         check_found_path(
1052             r#"
1053 //- /main.rs crate:main deps:dep
1054 $0
1055 //- /dep.rs crate:dep
1056 "#,
1057             "dep",
1058             "dep",
1059             "dep",
1060             "dep",
1061         );
1062
1063         check_found_path(
1064             r#"
1065 //- /main.rs crate:main deps:dep
1066 fn f() {
1067     fn inner() {}
1068     $0
1069 }
1070 //- /dep.rs crate:dep
1071 "#,
1072             "dep",
1073             "dep",
1074             "dep",
1075             "dep",
1076         );
1077     }
1078
1079     #[test]
1080     fn prelude_with_inner_items() {
1081         check_found_path(
1082             r#"
1083 //- /main.rs crate:main deps:std
1084 fn f() {
1085     fn inner() {}
1086     $0
1087 }
1088 //- /std.rs crate:std
1089 pub mod prelude {
1090     pub mod rust_2018 {
1091         pub enum Option { None }
1092         pub use Option::*;
1093     }
1094 }
1095         "#,
1096             "None",
1097             "None",
1098             "None",
1099             "None",
1100         );
1101     }
1102 }