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