1 //! An algorithm to find a path to refer to a certain item.
3 use std
::{cmp::Ordering, iter}
;
5 use hir_expand
::name
::{known, AsName, Name}
;
6 use rustc_hash
::FxHashSet
;
12 path
::{ModPath, PathKind}
,
13 visibility
::Visibility
,
14 CrateRootModuleId
, ModuleDefId
, ModuleId
,
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.
24 ) -> Option
<ModPath
> {
25 let _p
= profile
::span("find_path");
26 find_path_inner(db
, item
, from
, None
, prefer_no_std
)
29 pub fn find_path_prefixed(
33 prefix_kind
: PrefixKind
,
35 ) -> Option
<ModPath
> {
36 let _p
= profile
::span("find_path_prefixed");
37 find_path_inner(db
, item
, from
, Some(prefix_kind
), prefer_no_std
)
40 #[derive(Copy, Clone, Debug)]
47 fn zip_stability(a
: Stability
, b
: Stability
) -> Stability
{
49 (Stable
, Stable
) => Stable
,
54 const MAX_PATH_LEN
: usize = 15;
56 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
58 /// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
59 /// This is the same as plain, just that paths will start with `self` prepended if the path
60 /// starts with an identifier that is not a crate.
62 /// Causes paths to ignore imports in the local module.
64 /// Causes paths to start with `crate` where applicable, effectively forcing paths to be absolute.
70 fn prefix(self) -> PathKind
{
72 PrefixKind
::BySelf
=> PathKind
::Super(0),
73 PrefixKind
::Plain
=> PathKind
::Plain
,
74 PrefixKind
::ByCrate
=> PathKind
::Crate
,
79 fn is_absolute(&self) -> bool
{
80 self == &PrefixKind
::ByCrate
84 /// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
89 prefixed
: Option
<PrefixKind
>,
91 ) -> Option
<ModPath
> {
92 // - if the item is a builtin, it's in scope
93 if let ItemInNs
::Types(ModuleDefId
::BuiltinType(builtin
)) = item
{
94 return Some(ModPath
::from_segments(PathKind
::Plain
, Some(builtin
.as_name())));
97 let def_map
= from
.def_map(db
);
98 let crate_root
= def_map
.crate_root();
99 // - if the item is a module, jump straight to module search
100 if let ItemInNs
::Types(ModuleDefId
::ModuleId(module_id
)) = item
{
101 let mut visited_modules
= FxHashSet
::default();
102 return find_path_for_module(
105 &mut visited_modules
,
111 prefer_no_std
|| db
.crate_supports_no_std(crate_root
.krate
),
113 .map(|(item
, _
)| item
);
116 // - if the item is already in scope, return the name under which it is
117 let scope_name
= find_in_scope(db
, &def_map
, from
, item
);
118 if prefixed
.is_none() {
119 if let Some(scope_name
) = scope_name
{
120 return Some(ModPath
::from_segments(PathKind
::Plain
, Some(scope_name
)));
124 // - if the item is in the prelude, return the name from there
125 if let value @
Some(_
) = find_in_prelude(db
, &crate_root
.def_map(db
), &def_map
, item
, from
) {
129 if let Some(ModuleDefId
::EnumVariantId(variant
)) = item
.as_module_def_id() {
130 // - if the item is an enum variant, refer to it via the enum
131 if let Some(mut path
) = find_path_inner(
133 ItemInNs
::Types(variant
.parent
.into()),
138 let data
= db
.enum_data(variant
.parent
);
139 path
.push_segment(data
.variants
[variant
.local_id
].name
.clone());
142 // If this doesn't work, it seems we have no way of referring to the
143 // enum; that's very weird, but there might still be a reexport of the
147 let mut visited_modules
= FxHashSet
::default();
152 &mut visited_modules
,
158 prefer_no_std
|| db
.crate_supports_no_std(crate_root
.krate
),
161 .map(|(item
, _
)| item
)
164 fn find_path_for_module(
165 db
: &dyn DefDatabase
,
167 visited_modules
: &mut FxHashSet
<ModuleId
>,
168 crate_root
: CrateRootModuleId
,
172 prefixed
: Option
<PrefixKind
>,
174 ) -> Option
<(ModPath
, Stability
)> {
180 // - if the item is already in scope, return the name under which it is
181 let scope_name
= find_in_scope(db
, def_map
, from
, ItemInNs
::Types(module_id
.into()));
182 if prefixed
.is_none() {
183 if let Some(scope_name
) = scope_name
{
184 return Some((ModPath
::from_segments(PathKind
::Plain
, Some(scope_name
)), Stable
));
188 // - if the item is the crate root, return `crate`
189 if module_id
== crate_root
{
190 return Some((ModPath
::from_segments(PathKind
::Crate
, None
), Stable
));
193 // - if relative paths are fine, check if we are searching for a parent
194 if prefixed
.filter(PrefixKind
::is_absolute
).is_none() {
195 if let modpath @
Some(_
) = find_self_super(def_map
, module_id
, from
) {
196 return modpath
.zip(Some(Stable
));
200 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
201 let root_def_map
= crate_root
.def_map(db
);
202 for (name
, (def_id
, _extern_crate
)) in root_def_map
.extern_prelude() {
203 if module_id
== def_id
{
204 let name
= scope_name
.unwrap_or_else(|| name
.clone());
206 let name_already_occupied_in_type_ns
= def_map
207 .with_ancestor_maps(db
, from
.local_id
, &mut |def_map
, local_id
| {
211 .filter(|&(id
, _
)| id
!= ModuleDefId
::ModuleId(def_id
.into()))
214 let kind
= if name_already_occupied_in_type_ns
{
215 cov_mark
::hit
!(ambiguous_crate_start
);
220 return Some((ModPath
::from_segments(kind
, Some(name
)), Stable
));
224 if let value @
Some(_
) =
225 find_in_prelude(db
, &root_def_map
, &def_map
, ItemInNs
::Types(module_id
.into()), from
)
227 return value
.zip(Some(Stable
));
235 ItemInNs
::Types(module_id
.into()),
243 // FIXME: Do we still need this now that we record import origins, and hence aliases?
245 db
: &dyn DefDatabase
,
250 def_map
.with_ancestor_maps(db
, from
.local_id
, &mut |def_map
, local_id
| {
251 def_map
[local_id
].scope
.name_of(item
).map(|(name
, _
)| name
.clone())
255 /// Returns single-segment path (i.e. without any prefix) if `item` is found in prelude and its
256 /// name doesn't clash in current scope.
258 db
: &dyn DefDatabase
,
259 root_def_map
: &DefMap
,
260 local_def_map
: &DefMap
,
263 ) -> Option
<ModPath
> {
264 let (prelude_module
, _
) = root_def_map
.prelude()?
;
265 // Preludes in block DefMaps are ignored, only the crate DefMap is searched
266 let prelude_def_map
= prelude_module
.def_map(db
);
267 let prelude_scope
= &prelude_def_map
[prelude_module
.local_id
].scope
;
268 let (name
, vis
) = prelude_scope
.name_of(item
)?
;
269 if !vis
.is_visible_from(db
, from
) {
273 // Check if the name is in current scope and it points to the same def.
274 let found_and_same_def
=
275 local_def_map
.with_ancestor_maps(db
, from
.local_id
, &mut |def_map
, local_id
| {
276 let per_ns
= def_map
[local_id
].scope
.get(name
);
277 let same_def
= match item
{
278 ItemInNs
::Types(it
) => per_ns
.take_types()?
== it
,
279 ItemInNs
::Values(it
) => per_ns
.take_values()?
== it
,
280 ItemInNs
::Macros(it
) => per_ns
.take_macros()?
== it
,
285 if found_and_same_def
.unwrap_or(true) {
286 Some(ModPath
::from_segments(PathKind
::Plain
, Some(name
.clone())))
292 fn find_self_super(def_map
: &DefMap
, item
: ModuleId
, from
: ModuleId
) -> Option
<ModPath
> {
294 // - if the item is the module we're in, use `self`
295 Some(ModPath
::from_segments(PathKind
::Super(0), None
))
296 } else if let Some(parent_id
) = def_map
[from
.local_id
].parent
{
297 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
298 let parent_id
= def_map
.module_id(parent_id
);
299 if item
== parent_id
{
300 Some(ModPath
::from_segments(PathKind
::Super(1), None
))
309 fn calculate_best_path(
310 db
: &dyn DefDatabase
,
312 visited_modules
: &mut FxHashSet
<ModuleId
>,
313 crate_root
: CrateRootModuleId
,
317 mut prefixed
: Option
<PrefixKind
>,
319 scope_name
: Option
<Name
>,
320 ) -> Option
<(ModPath
, Stability
)> {
324 let mut best_path
= None
;
325 let update_best_path
=
326 |best_path
: &mut Option
<_
>, new_path
: (ModPath
, Stability
)| match best_path
{
327 Some((old_path
, old_stability
)) => {
328 *old_path
= new_path
.0;
329 *old_stability
= zip_stability(*old_stability
, new_path
.1);
331 None
=> *best_path
= Some(new_path
),
334 // - otherwise, look for modules containing (reexporting) it and import it from one of those
335 if item
.krate(db
) == Some(from
.krate
) {
336 let mut best_path_len
= max_len
;
337 // Item was defined in the same crate that wants to import it. It cannot be found in any
338 // dependency in this case.
339 for (module_id
, name
) in find_local_import_locations(db
, item
, from
) {
340 if !visited_modules
.insert(module_id
) {
341 cov_mark
::hit
!(recursive_imports
);
344 if let Some(mut path
) = find_path_for_module(
355 path
.0.push_segment(name
);
357 let new_path
= match best_path
.take() {
358 Some(best_path
) => select_best_path(best_path
, path
, prefer_no_std
),
361 best_path_len
= new_path
.0.len();
362 update_best_path(&mut best_path
, new_path
);
366 // Item was defined in some upstream crate. This means that it must be exported from one,
367 // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
368 // that wants to import it here, but we always prefer to use the external path here.
370 let crate_graph
= db
.crate_graph();
371 let extern_paths
= crate_graph
[from
.krate
].dependencies
.iter().filter_map(|dep
| {
372 let import_map
= db
.import_map(dep
.crate_id
);
373 import_map
.import_info_for(item
).and_then(|info
| {
374 if info
.is_doc_hidden
{
375 // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
379 // Determine best path for containing module and append last segment from `info`.
380 // FIXME: we should guide this to look up the path locally, or from the same crate again?
381 let (mut path
, path_stability
) = find_path_for_module(
392 cov_mark
::hit
!(partially_imported
);
393 path
.push_segment(info
.name
.clone());
396 zip_stability(path_stability
, if info
.is_unstable { Unstable }
else { Stable }
),
401 for path
in extern_paths
{
402 let new_path
= match best_path
.take() {
403 Some(best_path
) => select_best_path(best_path
, path
, prefer_no_std
),
406 update_best_path(&mut best_path
, new_path
);
409 if let Some(module
) = item
.module(db
) {
410 if module
.containing_block().is_some() && prefixed
.is_some() {
411 cov_mark
::hit
!(prefixed_in_block_expression
);
412 prefixed
= Some(PrefixKind
::Plain
);
415 match prefixed
.map(PrefixKind
::prefix
) {
416 Some(prefix
) => best_path
.or_else(|| {
417 scope_name
.map(|scope_name
| (ModPath
::from_segments(prefix
, Some(scope_name
)), Stable
))
424 old_path
: (ModPath
, Stability
),
425 new_path
: (ModPath
, Stability
),
427 ) -> (ModPath
, Stability
) {
428 match (old_path
.1, new_path
.1) {
429 (Stable
, Unstable
) => return old_path
,
430 (Unstable
, Stable
) => return new_path
,
433 const STD_CRATES
: [Name
; 3] = [known
::std
, known
::core
, known
::alloc
];
434 match (old_path
.0.segments().first(), new_path
.0.segments().first()) {
435 (Some(old
), Some(new
)) if STD_CRATES
.contains(old
) && STD_CRATES
.contains(new
) => {
436 let rank
= match prefer_no_std
{
437 false => |name
: &Name
| match name
{
438 name
if name
== &known
::core
=> 0,
439 name
if name
== &known
::alloc
=> 0,
440 name
if name
== &known
::std
=> 1,
443 true => |name
: &Name
| match name
{
444 name
if name
== &known
::core
=> 2,
445 name
if name
== &known
::alloc
=> 1,
446 name
if name
== &known
::std
=> 0,
450 let nrank
= rank(new
);
451 let orank
= rank(old
);
452 match nrank
.cmp(&orank
) {
453 Ordering
::Less
=> old_path
,
455 if new_path
.0.len() < old_path
.0.len() {
461 Ordering
::Greater
=> new_path
,
465 if new_path
.0.len() < old_path
.0.len() {
474 // FIXME: Remove allocations
475 /// Finds locations in `from.krate` from which `item` can be imported by `from`.
476 fn find_local_import_locations(
477 db
: &dyn DefDatabase
,
480 ) -> Vec
<(ModuleId
, Name
)> {
481 let _p
= profile
::span("find_local_import_locations");
483 // `from` can import anything below `from` with visibility of at least `from`, and anything
484 // above `from` with any visibility. That means we do not need to descend into private siblings
485 // of `from` (and similar).
487 let def_map
= from
.def_map(db
);
489 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
490 // of its (recursive) parent modules.
491 let data
= &def_map
[from
.local_id
];
493 data
.children
.values().map(|child
| def_map
.module_id(*child
)).collect
::<Vec
<_
>>();
494 // FIXME: do we need to traverse out of block expressions here?
495 for ancestor
in iter
::successors(from
.containing_module(db
), |m
| m
.containing_module(db
)) {
496 worklist
.push(ancestor
);
499 let def_map
= def_map
.crate_root().def_map(db
);
501 let mut seen
: FxHashSet
<_
> = FxHashSet
::default();
503 let mut locations
= Vec
::new();
504 while let Some(module
) = worklist
.pop() {
505 if !seen
.insert(module
) {
506 continue; // already processed this module
510 let data
= if module
.krate
== from
.krate
{
511 if module
.block
.is_some() {
512 // Re-query the block's DefMap
513 ext_def_map
= module
.def_map(db
);
514 &ext_def_map
[module
.local_id
]
516 // Reuse the root DefMap
517 &def_map
[module
.local_id
]
520 // The crate might reexport a module defined in another crate.
521 ext_def_map
= module
.def_map(db
);
522 &ext_def_map
[module
.local_id
]
525 if let Some((name
, vis
)) = data
.scope
.name_of(item
) {
526 if vis
.is_visible_from(db
, from
) {
527 let is_private
= match vis
{
528 Visibility
::Module(private_to
) => private_to
.local_id
== module
.local_id
,
529 Visibility
::Public
=> false,
531 let is_original_def
= match item
.as_module_def_id() {
532 Some(module_def_id
) => data
.scope
.declarations().any(|it
| it
== module_def_id
),
536 // Ignore private imports. these could be used if we are
537 // in a submodule of this module, but that's usually not
538 // what the user wants; and if this module can import
539 // the item and we're a submodule of it, so can we.
540 // Also this keeps the cached data smaller.
541 if !is_private
|| is_original_def
{
542 locations
.push((module
, name
.clone()));
547 // Descend into all modules visible from `from`.
548 for (ty
, vis
) in data
.scope
.types() {
549 if let ModuleDefId
::ModuleId(module
) = ty
{
550 if vis
.is_visible_from(db
, from
) {
551 worklist
.push(module
);
562 use base_db
::fixture
::WithFixture
;
563 use hir_expand
::hygiene
::Hygiene
;
564 use syntax
::ast
::AstNode
;
566 use crate::test_db
::TestDB
;
570 /// `code` needs to contain a cursor marker; checks that `find_path` for the
571 /// item the `path` refers to returns that same path when called from the
572 /// module the cursor is in.
573 fn check_found_path_(ra_fixture
: &str, path
: &str, prefix_kind
: Option
<PrefixKind
>) {
574 let (db
, pos
) = TestDB
::with_position(ra_fixture
);
575 let module
= db
.module_at_position(pos
);
576 let parsed_path_file
= syntax
::SourceFile
::parse(&format
!("use {path};"));
578 parsed_path_file
.syntax_node().descendants().find_map(syntax
::ast
::Path
::cast
).unwrap();
579 let mod_path
= ModPath
::from_src(&db
, ast_path
, &Hygiene
::new_unhygienic()).unwrap();
581 let def_map
= module
.def_map(&db
);
582 let resolved
= def_map
587 crate::item_scope
::BuiltinShadowMode
::Module
,
595 find_path_inner(&db
, ItemInNs
::Types(resolved
), module
, prefix_kind
, false);
596 assert_eq
!(found_path
, Some(mod_path
), "{prefix_kind:?}");
606 check_found_path_(ra_fixture
, unprefixed
, None
);
607 check_found_path_(ra_fixture
, prefixed
, Some(PrefixKind
::Plain
));
608 check_found_path_(ra_fixture
, absolute
, Some(PrefixKind
::ByCrate
));
609 check_found_path_(ra_fixture
, self_prefixed
, Some(PrefixKind
::BySelf
));
725 fn different_crate() {
728 //- /main.rs crate:main deps:std
730 //- /std.rs crate:std
741 fn different_crate_renamed() {
744 //- /main.rs crate:main deps:std
745 extern crate std as std_renamed;
747 //- /std.rs crate:std
758 fn partially_imported() {
759 cov_mark
::check
!(partially_imported
);
760 // Tests that short paths are used even for external items, when parts of the path are
764 //- /main.rs crate:main deps:syntax
769 //- /lib.rs crate:syntax
771 pub enum ModuleItem {
777 "syntax::ast::ModuleItem",
778 "syntax::ast::ModuleItem",
779 "syntax::ast::ModuleItem",
784 //- /main.rs crate:main deps:syntax
787 //- /lib.rs crate:syntax
789 pub enum ModuleItem {
794 "syntax::ast::ModuleItem",
795 "syntax::ast::ModuleItem",
796 "syntax::ast::ModuleItem",
797 "syntax::ast::ModuleItem",
802 fn same_crate_reexport() {
806 mod foo { pub(super) struct S; }
807 pub(crate) use foo::*;
819 fn same_crate_reexport_rename() {
823 mod foo { pub(super) struct S; }
824 pub(crate) use foo::S as U;
836 fn different_crate_reexport() {
839 //- /main.rs crate:main deps:std
841 //- /std.rs crate:std deps:core
843 //- /core.rs crate:core
857 //- /main.rs edition:2018 crate:main deps:std
859 //- /std.rs crate:std
874 fn shadowed_prelude() {
877 //- /main.rs crate:main deps:std
880 //- /std.rs crate:std
887 "std::prelude::rust_2018::S",
888 "std::prelude::rust_2018::S",
889 "std::prelude::rust_2018::S",
890 "std::prelude::rust_2018::S",
895 fn imported_prelude() {
898 //- /main.rs edition:2018 crate:main deps:std
901 //- /std.rs crate:std
916 fn enum_variant_from_prelude() {
918 //- /main.rs edition:2018 crate:main deps:std
920 //- /std.rs crate:std
923 pub enum Option<T> { Some(T), None }
928 check_found_path(code
, "None", "None", "None", "None");
929 check_found_path(code
, "Some", "Some", "Some", "Some");
942 pub mod bar { pub struct S; }
944 pub use crate::foo::bar::S;
954 fn discount_private_imports() {
959 pub mod bar { pub struct S; }
964 // crate::S would be shorter, but using private imports seems wrong
996 fn prefer_std_paths_over_alloc() {
999 //- /main.rs crate:main deps:alloc,std
1002 //- /std.rs crate:std deps:alloc
1004 pub use alloc::sync::Arc;
1007 //- /zzz.rs crate:alloc
1020 fn prefer_core_paths_over_std() {
1023 //- /main.rs crate:main deps:core,std
1028 //- /std.rs crate:std deps:core
1031 pub use core::fmt::Error;
1034 //- /zzz.rs crate:core
1046 // Should also work (on a best-effort basis) if `no_std` is conditional.
1049 //- /main.rs crate:main deps:core,std
1050 #![cfg_attr(not(test), no_std)]
1054 //- /std.rs crate:std deps:core
1057 pub use core::fmt::Error;
1060 //- /zzz.rs crate:core
1074 fn prefer_alloc_paths_over_std() {
1077 //- /main.rs crate:main deps:alloc,std
1082 //- /std.rs crate:std deps:alloc
1085 pub use alloc::sync::Arc;
1088 //- /zzz.rs crate:alloc
1102 fn prefer_shorter_paths_if_not_alloc() {
1105 //- /main.rs crate:main deps:megaalloc,std
1108 //- /std.rs crate:std deps:megaalloc
1110 pub use megaalloc::sync::Arc;
1113 //- /zzz.rs crate:megaalloc
1124 fn builtins_are_in_scope() {
1132 check_found_path(code
, "u8", "u8", "u8", "u8");
1133 check_found_path(code
, "u16", "u16", "u16", "u16");
1153 fn inner_items_from_outer_scope() {
1171 fn inner_items_from_inner_module() {
1172 cov_mark
::check
!(prefixed_in_block_expression
);
1192 fn outer_items_with_inner_items_present() {
1196 pub struct CompleteMe;
1204 // FIXME: these could use fewer/better prefixes
1205 "module::CompleteMe",
1206 "crate::module::CompleteMe",
1207 "crate::module::CompleteMe",
1208 "crate::module::CompleteMe",
1213 fn from_inside_module() {
1214 // This worked correctly, but the test suite logic was broken.
1215 cov_mark
::check
!(submodule_in_testdb
);
1236 fn from_inside_module_with_inner_items() {
1258 fn recursive_pub_mod_reexport() {
1259 cov_mark
::check
!(recursive_imports
);
1263 let _ = 22_i32.as_name$0();
1268 fn as_name(&self) -> String;
1270 impl AsName for i32 {
1271 fn as_name(&self) -> String {
1272 format!("Name: {}", self)
1275 pub use crate::name;
1280 "crate::name::AsName",
1281 "self::name::AsName",
1289 //- /main.rs crate:main deps:dep
1291 //- /dep.rs crate:dep
1301 //- /main.rs crate:main deps:dep
1306 //- /dep.rs crate:dep
1316 fn prelude_with_inner_items() {
1319 //- /main.rs edition:2018 crate:main deps:std
1324 //- /std.rs crate:std
1327 pub enum Option { None }
1340 fn different_crate_renamed_through_dep() {
1343 //- /main.rs crate:main deps:intermediate
1345 //- /intermediate.rs crate:intermediate deps:std
1346 pub extern crate std as std_renamed;
1347 //- /std.rs crate:std
1350 "intermediate::std_renamed::S",
1351 "intermediate::std_renamed::S",
1352 "intermediate::std_renamed::S",
1353 "intermediate::std_renamed::S",
1358 fn different_crate_doc_hidden() {
1361 //- /main.rs crate:main deps:intermediate
1363 //- /intermediate.rs crate:intermediate deps:std
1365 pub extern crate std;
1366 pub extern crate std as longer;
1367 //- /std.rs crate:std
1370 "intermediate::longer::S",
1371 "intermediate::longer::S",
1372 "intermediate::longer::S",
1373 "intermediate::longer::S",
1378 fn respect_doc_hidden() {
1381 //- /main.rs crate:main deps:std,lazy_static
1383 //- /lazy_static.rs crate:lazy_static deps:core
1385 pub use core::ops::Deref as __Deref;
1386 //- /std.rs crate:std deps:core
1388 //- /core.rs crate:core
1401 fn respect_unstable_modules() {
1404 //- /main.rs crate:main deps:std,core
1408 //- /longer.rs crate:std deps:core
1410 pub use core::error::Error;
1412 //- /core.rs crate:core
1414 #![unstable(feature = "error_in_core", issue = "103765")]
1418 "std::error::Error",
1419 "std::error::Error",
1420 "std::error::Error",
1421 "std::error::Error",