]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir-def / src / find_path.rs
CommitLineData
064997fb
FG
1//! An algorithm to find a path to refer to a certain item.
2
2b03887a 3use std::{cmp::Ordering, iter};
064997fb
FG
4
5use hir_expand::name::{known, AsName, Name};
6use rustc_hash::FxHashSet;
7
8use 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.
2b03887a
FG
19pub fn find_path(
20 db: &dyn DefDatabase,
21 item: ItemInNs,
22 from: ModuleId,
23 prefer_no_std: bool,
24) -> Option<ModPath> {
064997fb 25 let _p = profile::span("find_path");
2b03887a 26 find_path_inner(db, item, from, None, prefer_no_std)
064997fb
FG
27}
28
29pub fn find_path_prefixed(
30 db: &dyn DefDatabase,
31 item: ItemInNs,
32 from: ModuleId,
33 prefix_kind: PrefixKind,
2b03887a 34 prefer_no_std: bool,
064997fb
FG
35) -> Option<ModPath> {
36 let _p = profile::span("find_path_prefixed");
2b03887a 37 find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std)
064997fb
FG
38}
39
40const MAX_PATH_LEN: usize = 15;
41
064997fb
FG
42#[derive(Copy, Clone, Debug, PartialEq, Eq)]
43pub 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` iprepended f 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
54impl PrefixKind {
55 #[inline]
56 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]
65 fn is_absolute(&self) -> bool {
66 self == &PrefixKind::ByCrate
67 }
68}
2b03887a 69
064997fb
FG
70/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
71fn find_path_inner(
72 db: &dyn DefDatabase,
73 item: ItemInNs,
74 from: ModuleId,
75 prefixed: Option<PrefixKind>,
2b03887a 76 prefer_no_std: bool,
064997fb 77) -> Option<ModPath> {
2b03887a
FG
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 }
064997fb 82
064997fb 83 let def_map = from.def_map(db);
2b03887a
FG
84 let crate_root = def_map.crate_root(db);
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
9c376795 110 if let value @ Some(_) = find_in_prelude(db, &crate_root.def_map(db), &def_map, item, from) {
2b03887a
FG
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 )
064997fb
FG
146}
147
2b03887a 148fn find_path_for_module(
064997fb
FG
149 db: &dyn DefDatabase,
150 def_map: &DefMap,
2b03887a
FG
151 visited_modules: &mut FxHashSet<ModuleId>,
152 crate_root: ModuleId,
064997fb 153 from: ModuleId,
2b03887a 154 module_id: ModuleId,
064997fb 155 max_len: usize,
2b03887a
FG
156 prefixed: Option<PrefixKind>,
157 prefer_no_std: bool,
064997fb
FG
158) -> Option<ModPath> {
159 if max_len == 0 {
160 return None;
161 }
162
163 // Base cases:
064997fb 164 // - if the item is already in scope, return the name under which it is
2b03887a 165 let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into()));
064997fb
FG
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
064997fb 172 // - if the item is the crate root, return `crate`
2b03887a 173 if module_id == crate_root {
064997fb
FG
174 return Some(ModPath::from_segments(PathKind::Crate, None));
175 }
176
2b03887a 177 // - if relative paths are fine, check if we are searching for a parent
064997fb 178 if prefixed.filter(PrefixKind::is_absolute).is_none() {
9c376795 179 if let modpath @ Some(_) = find_self_super(def_map, module_id, from) {
064997fb
FG
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);
2b03887a
FG
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)));
064997fb
FG
205 }
206 }
207
9c376795
FG
208 if let value @ Some(_) =
209 find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from)
2b03887a
FG
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
227fn 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
9c376795
FG
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.
2b03887a
FG
240fn find_in_prelude(
241 db: &dyn DefDatabase,
242 root_def_map: &DefMap,
9c376795 243 local_def_map: &DefMap,
2b03887a
FG
244 item: ItemInNs,
245 from: ModuleId,
9c376795
FG
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
064997fb 272 }
2b03887a 273}
064997fb 274
2b03887a
FG
275fn 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
064997fb 286 }
2b03887a
FG
287 } else {
288 None
064997fb 289 }
2b03887a 290}
064997fb 291
2b03887a
FG
292fn 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 }
064997fb 307 let mut best_path = None;
2b03887a
FG
308 // Recursive case:
309 // - otherwise, look for modules containing (reexporting) it and import it from one of those
064997fb 310 if item.krate(db) == Some(from.krate) {
2b03887a 311 let mut best_path_len = max_len;
064997fb
FG
312 // Item was defined in the same crate that wants to import it. It cannot be found in any
313 // dependency in this case.
064997fb
FG
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 }
2b03887a 319 if let Some(mut path) = find_path_for_module(
064997fb
FG
320 db,
321 def_map,
2b03887a
FG
322 visited_modules,
323 crate_root,
064997fb 324 from,
2b03887a 325 module_id,
064997fb
FG
326 best_path_len - 1,
327 prefixed,
2b03887a 328 prefer_no_std,
064997fb
FG
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?
2b03887a 351 let mut path = find_path_for_module(
064997fb
FG
352 db,
353 def_map,
2b03887a
FG
354 visited_modules,
355 crate_root,
064997fb 356 from,
2b03887a
FG
357 info.container,
358 max_len - 1,
064997fb 359 prefixed,
2b03887a 360 prefer_no_std,
064997fb
FG
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 }
2b03887a
FG
376 if let Some(module) = item.module(db) {
377 if module.def_map(db).block_id().is_some() && prefixed.is_some() {
064997fb
FG
378 cov_mark::hit!(prefixed_in_block_expression);
379 prefixed = Some(PrefixKind::Plain);
380 }
381 }
064997fb
FG
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
390fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
2b03887a
FG
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 }
064997fb 421 }
2b03887a
FG
422 _ => {
423 if new_path.len() < old_path.len() {
424 new_path
425 } else {
426 old_path
427 }
064997fb 428 }
064997fb
FG
429 }
430}
431
2b03887a 432// FIXME: Remove allocations
064997fb
FG
433/// Finds locations in `from.krate` from which `item` can be imported by `from`.
434fn 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(db).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)]
519mod 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.
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);
9c376795 534 let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};"));
064997fb
FG
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 )
547 .0
548 .take_types()
549 .unwrap();
550
2b03887a
FG
551 let found_path =
552 find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind, false);
9c376795 553 assert_eq!(found_path, Some(mod_path), "{prefix_kind:?}");
064997fb
FG
554 }
555
556 fn check_found_path(
557 ra_fixture: &str,
558 unprefixed: &str,
559 prefixed: &str,
560 absolute: &str,
561 self_prefixed: &str,
562 ) {
563 check_found_path_(ra_fixture, unprefixed, None);
564 check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain));
565 check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate));
566 check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf));
567 }
568
569 #[test]
570 fn same_module() {
571 check_found_path(
572 r#"
573struct S;
574$0
575 "#,
576 "S",
577 "S",
578 "crate::S",
579 "self::S",
580 );
581 }
582
583 #[test]
584 fn enum_variant() {
585 check_found_path(
586 r#"
587enum E { A }
588$0
589 "#,
590 "E::A",
591 "E::A",
2b03887a
FG
592 "crate::E::A",
593 "self::E::A",
064997fb
FG
594 );
595 }
596
597 #[test]
598 fn sub_module() {
599 check_found_path(
600 r#"
601mod foo {
602 pub struct S;
603}
604$0
605 "#,
606 "foo::S",
607 "foo::S",
608 "crate::foo::S",
609 "self::foo::S",
610 );
611 }
612
613 #[test]
614 fn super_module() {
615 check_found_path(
616 r#"
617//- /main.rs
618mod foo;
619//- /foo.rs
620mod bar;
621struct S;
622//- /foo/bar.rs
623$0
624 "#,
625 "super::S",
626 "super::S",
627 "crate::foo::S",
628 "super::S",
629 );
630 }
631
632 #[test]
633 fn self_module() {
634 check_found_path(
635 r#"
636//- /main.rs
637mod foo;
638//- /foo.rs
639$0
640 "#,
641 "self",
642 "self",
643 "crate::foo",
644 "self",
645 );
646 }
647
648 #[test]
649 fn crate_root() {
650 check_found_path(
651 r#"
652//- /main.rs
653mod foo;
654//- /foo.rs
655$0
656 "#,
657 "crate",
658 "crate",
659 "crate",
660 "crate",
661 );
662 }
663
664 #[test]
665 fn same_crate() {
666 check_found_path(
667 r#"
668//- /main.rs
669mod foo;
670struct S;
671//- /foo.rs
672$0
673 "#,
674 "crate::S",
675 "crate::S",
676 "crate::S",
677 "crate::S",
678 );
679 }
680
681 #[test]
682 fn different_crate() {
683 check_found_path(
684 r#"
685//- /main.rs crate:main deps:std
686$0
687//- /std.rs crate:std
688pub struct S;
689 "#,
690 "std::S",
691 "std::S",
692 "std::S",
693 "std::S",
694 );
695 }
696
697 #[test]
698 fn different_crate_renamed() {
699 check_found_path(
700 r#"
701//- /main.rs crate:main deps:std
702extern crate std as std_renamed;
703$0
704//- /std.rs crate:std
705pub struct S;
706 "#,
707 "std_renamed::S",
708 "std_renamed::S",
709 "std_renamed::S",
710 "std_renamed::S",
711 );
712 }
713
714 #[test]
715 fn partially_imported() {
716 cov_mark::check!(partially_imported);
717 // Tests that short paths are used even for external items, when parts of the path are
718 // already in scope.
719 check_found_path(
720 r#"
721//- /main.rs crate:main deps:syntax
722
723use syntax::ast;
724$0
725
726//- /lib.rs crate:syntax
727pub mod ast {
728 pub enum ModuleItem {
729 A, B, C,
730 }
731}
732 "#,
733 "ast::ModuleItem",
734 "syntax::ast::ModuleItem",
735 "syntax::ast::ModuleItem",
736 "syntax::ast::ModuleItem",
737 );
738
739 check_found_path(
740 r#"
741//- /main.rs crate:main deps:syntax
742$0
743
744//- /lib.rs crate:syntax
745pub mod ast {
746 pub enum ModuleItem {
747 A, B, C,
748 }
749}
750 "#,
751 "syntax::ast::ModuleItem",
752 "syntax::ast::ModuleItem",
753 "syntax::ast::ModuleItem",
754 "syntax::ast::ModuleItem",
755 );
756 }
757
758 #[test]
759 fn same_crate_reexport() {
760 check_found_path(
761 r#"
762mod bar {
763 mod foo { pub(super) struct S; }
764 pub(crate) use foo::*;
765}
766$0
767 "#,
768 "bar::S",
769 "bar::S",
770 "crate::bar::S",
771 "self::bar::S",
772 );
773 }
774
775 #[test]
776 fn same_crate_reexport_rename() {
777 check_found_path(
778 r#"
779mod bar {
780 mod foo { pub(super) struct S; }
781 pub(crate) use foo::S as U;
782}
783$0
784 "#,
785 "bar::U",
786 "bar::U",
787 "crate::bar::U",
788 "self::bar::U",
789 );
790 }
791
792 #[test]
793 fn different_crate_reexport() {
794 check_found_path(
795 r#"
796//- /main.rs crate:main deps:std
797$0
798//- /std.rs crate:std deps:core
799pub use core::S;
800//- /core.rs crate:core
801pub struct S;
802 "#,
803 "std::S",
804 "std::S",
805 "std::S",
806 "std::S",
807 );
808 }
809
810 #[test]
811 fn prelude() {
812 check_found_path(
813 r#"
9ffffee4 814//- /main.rs edition:2018 crate:main deps:std
064997fb
FG
815$0
816//- /std.rs crate:std
817pub mod prelude {
818 pub mod rust_2018 {
819 pub struct S;
820 }
821}
822 "#,
823 "S",
824 "S",
825 "S",
826 "S",
827 );
828 }
829
9c376795
FG
830 #[test]
831 fn shadowed_prelude() {
832 check_found_path(
833 r#"
834//- /main.rs crate:main deps:std
835struct S;
836$0
837//- /std.rs crate:std
838pub mod prelude {
839 pub mod rust_2018 {
840 pub struct S;
841 }
842}
843"#,
844 "std::prelude::rust_2018::S",
845 "std::prelude::rust_2018::S",
846 "std::prelude::rust_2018::S",
847 "std::prelude::rust_2018::S",
848 );
849 }
850
851 #[test]
852 fn imported_prelude() {
853 check_found_path(
854 r#"
9ffffee4 855//- /main.rs edition:2018 crate:main deps:std
9c376795
FG
856use S;
857$0
858//- /std.rs crate:std
859pub mod prelude {
860 pub mod rust_2018 {
861 pub struct S;
862 }
863}
864"#,
865 "S",
866 "S",
867 "S",
868 "S",
869 );
870 }
871
064997fb
FG
872 #[test]
873 fn enum_variant_from_prelude() {
874 let code = r#"
9ffffee4 875//- /main.rs edition:2018 crate:main deps:std
064997fb
FG
876$0
877//- /std.rs crate:std
878pub mod prelude {
879 pub mod rust_2018 {
880 pub enum Option<T> { Some(T), None }
881 pub use Option::*;
882 }
883}
884 "#;
885 check_found_path(code, "None", "None", "None", "None");
886 check_found_path(code, "Some", "Some", "Some", "Some");
887 }
888
889 #[test]
890 fn shortest_path() {
891 check_found_path(
892 r#"
893//- /main.rs
894pub mod foo;
895pub mod baz;
896struct S;
897$0
898//- /foo.rs
899pub mod bar { pub struct S; }
900//- /baz.rs
901pub use crate::foo::bar::S;
902 "#,
903 "baz::S",
904 "baz::S",
905 "crate::baz::S",
906 "self::baz::S",
907 );
908 }
909
910 #[test]
911 fn discount_private_imports() {
912 check_found_path(
913 r#"
914//- /main.rs
915mod foo;
916pub mod bar { pub struct S; }
917use bar::S;
918//- /foo.rs
919$0
920 "#,
921 // crate::S would be shorter, but using private imports seems wrong
922 "crate::bar::S",
923 "crate::bar::S",
924 "crate::bar::S",
925 "crate::bar::S",
926 );
927 }
928
929 #[test]
930 fn import_cycle() {
931 check_found_path(
932 r#"
933//- /main.rs
934pub mod foo;
935pub mod bar;
936pub mod baz;
937//- /bar.rs
938$0
939//- /foo.rs
940pub use super::baz;
941pub struct S;
942//- /baz.rs
943pub use super::foo;
944 "#,
945 "crate::foo::S",
946 "crate::foo::S",
947 "crate::foo::S",
948 "crate::foo::S",
949 );
950 }
951
952 #[test]
953 fn prefer_std_paths_over_alloc() {
064997fb
FG
954 check_found_path(
955 r#"
956//- /main.rs crate:main deps:alloc,std
957$0
958
959//- /std.rs crate:std deps:alloc
960pub mod sync {
961 pub use alloc::sync::Arc;
962}
963
964//- /zzz.rs crate:alloc
965pub mod sync {
966 pub struct Arc;
967}
968 "#,
969 "std::sync::Arc",
970 "std::sync::Arc",
971 "std::sync::Arc",
972 "std::sync::Arc",
973 );
974 }
975
976 #[test]
977 fn prefer_core_paths_over_std() {
064997fb
FG
978 check_found_path(
979 r#"
980//- /main.rs crate:main deps:core,std
981#![no_std]
982
983$0
984
985//- /std.rs crate:std deps:core
986
987pub mod fmt {
988 pub use core::fmt::Error;
989}
990
991//- /zzz.rs crate:core
992
993pub mod fmt {
994 pub struct Error;
995}
996 "#,
997 "core::fmt::Error",
998 "core::fmt::Error",
999 "core::fmt::Error",
1000 "core::fmt::Error",
1001 );
1002
1003 // Should also work (on a best-effort basis) if `no_std` is conditional.
1004 check_found_path(
1005 r#"
1006//- /main.rs crate:main deps:core,std
1007#![cfg_attr(not(test), no_std)]
1008
1009$0
1010
1011//- /std.rs crate:std deps:core
1012
1013pub mod fmt {
1014 pub use core::fmt::Error;
1015}
1016
1017//- /zzz.rs crate:core
1018
1019pub mod fmt {
1020 pub struct Error;
1021}
1022 "#,
1023 "core::fmt::Error",
1024 "core::fmt::Error",
1025 "core::fmt::Error",
1026 "core::fmt::Error",
1027 );
1028 }
1029
1030 #[test]
1031 fn prefer_alloc_paths_over_std() {
1032 check_found_path(
1033 r#"
1034//- /main.rs crate:main deps:alloc,std
1035#![no_std]
1036
1037$0
1038
1039//- /std.rs crate:std deps:alloc
1040
1041pub mod sync {
1042 pub use alloc::sync::Arc;
1043}
1044
1045//- /zzz.rs crate:alloc
1046
1047pub mod sync {
1048 pub struct Arc;
1049}
1050 "#,
1051 "alloc::sync::Arc",
1052 "alloc::sync::Arc",
1053 "alloc::sync::Arc",
1054 "alloc::sync::Arc",
1055 );
1056 }
1057
1058 #[test]
1059 fn prefer_shorter_paths_if_not_alloc() {
1060 check_found_path(
1061 r#"
1062//- /main.rs crate:main deps:megaalloc,std
1063$0
1064
1065//- /std.rs crate:std deps:megaalloc
1066pub mod sync {
1067 pub use megaalloc::sync::Arc;
1068}
1069
1070//- /zzz.rs crate:megaalloc
1071pub struct Arc;
1072 "#,
1073 "megaalloc::Arc",
1074 "megaalloc::Arc",
1075 "megaalloc::Arc",
1076 "megaalloc::Arc",
1077 );
1078 }
1079
1080 #[test]
1081 fn builtins_are_in_scope() {
1082 let code = r#"
1083$0
1084
1085pub mod primitive {
1086 pub use u8;
1087}
1088 "#;
1089 check_found_path(code, "u8", "u8", "u8", "u8");
1090 check_found_path(code, "u16", "u16", "u16", "u16");
1091 }
1092
1093 #[test]
1094 fn inner_items() {
1095 check_found_path(
1096 r#"
1097fn main() {
1098 struct Inner {}
1099 $0
1100}
1101 "#,
1102 "Inner",
1103 "Inner",
1104 "Inner",
1105 "Inner",
1106 );
1107 }
1108
1109 #[test]
1110 fn inner_items_from_outer_scope() {
1111 check_found_path(
1112 r#"
1113fn main() {
1114 struct Struct {}
1115 {
1116 $0
1117 }
1118}
1119 "#,
1120 "Struct",
1121 "Struct",
1122 "Struct",
1123 "Struct",
1124 );
1125 }
1126
1127 #[test]
1128 fn inner_items_from_inner_module() {
1129 cov_mark::check!(prefixed_in_block_expression);
1130 check_found_path(
1131 r#"
1132fn main() {
1133 mod module {
1134 struct Struct {}
1135 }
1136 {
1137 $0
1138 }
1139}
1140 "#,
1141 "module::Struct",
1142 "module::Struct",
1143 "module::Struct",
1144 "module::Struct",
1145 );
1146 }
1147
1148 #[test]
1149 fn outer_items_with_inner_items_present() {
1150 check_found_path(
1151 r#"
1152mod module {
1153 pub struct CompleteMe;
1154}
1155
1156fn main() {
1157 fn inner() {}
1158 $0
1159}
1160 "#,
1161 // FIXME: these could use fewer/better prefixes
1162 "module::CompleteMe",
1163 "crate::module::CompleteMe",
1164 "crate::module::CompleteMe",
1165 "crate::module::CompleteMe",
1166 )
1167 }
1168
1169 #[test]
1170 fn from_inside_module() {
1171 // This worked correctly, but the test suite logic was broken.
1172 cov_mark::check!(submodule_in_testdb);
1173 check_found_path(
1174 r#"
1175mod baz {
1176 pub struct Foo {}
1177}
1178
1179mod bar {
1180 fn bar() {
1181 $0
1182 }
1183}
1184 "#,
1185 "crate::baz::Foo",
1186 "crate::baz::Foo",
1187 "crate::baz::Foo",
1188 "crate::baz::Foo",
1189 )
1190 }
1191
1192 #[test]
1193 fn from_inside_module_with_inner_items() {
1194 check_found_path(
1195 r#"
1196mod baz {
1197 pub struct Foo {}
1198}
1199
1200mod bar {
1201 fn bar() {
1202 fn inner() {}
1203 $0
1204 }
1205}
1206 "#,
1207 "crate::baz::Foo",
1208 "crate::baz::Foo",
1209 "crate::baz::Foo",
1210 "crate::baz::Foo",
1211 )
1212 }
1213
1214 #[test]
1215 fn recursive_pub_mod_reexport() {
1216 cov_mark::check!(recursive_imports);
1217 check_found_path(
1218 r#"
1219fn main() {
1220 let _ = 22_i32.as_name$0();
1221}
1222
1223pub mod name {
1224 pub trait AsName {
1225 fn as_name(&self) -> String;
1226 }
1227 impl AsName for i32 {
1228 fn as_name(&self) -> String {
1229 format!("Name: {}", self)
1230 }
1231 }
1232 pub use crate::name;
1233}
1234"#,
1235 "name::AsName",
1236 "name::AsName",
1237 "crate::name::AsName",
1238 "self::name::AsName",
1239 );
1240 }
1241
1242 #[test]
1243 fn extern_crate() {
1244 check_found_path(
1245 r#"
1246//- /main.rs crate:main deps:dep
1247$0
1248//- /dep.rs crate:dep
1249"#,
1250 "dep",
1251 "dep",
1252 "dep",
1253 "dep",
1254 );
1255
1256 check_found_path(
1257 r#"
1258//- /main.rs crate:main deps:dep
1259fn f() {
1260 fn inner() {}
1261 $0
1262}
1263//- /dep.rs crate:dep
1264"#,
1265 "dep",
1266 "dep",
1267 "dep",
1268 "dep",
1269 );
1270 }
1271
1272 #[test]
1273 fn prelude_with_inner_items() {
1274 check_found_path(
1275 r#"
9ffffee4 1276//- /main.rs edition:2018 crate:main deps:std
064997fb
FG
1277fn f() {
1278 fn inner() {}
1279 $0
1280}
1281//- /std.rs crate:std
1282pub mod prelude {
1283 pub mod rust_2018 {
1284 pub enum Option { None }
1285 pub use Option::*;
1286 }
1287}
1288 "#,
1289 "None",
1290 "None",
1291 "None",
1292 "None",
1293 );
1294 }
1295}