use hir_expand::name::{known, AsName, Name};
use rustc_hash::FxHashSet;
-use crate::nameres::DefMap;
use crate::{
db::DefDatabase,
item_scope::ItemInNs,
+ nameres::DefMap,
path::{ModPath, PathKind},
visibility::Visibility,
ModuleDefId, ModuleId,
/// *from where* you're referring to the item, hence the `from` parameter.
pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
let _p = profile::span("find_path");
- find_path_inner(db, item, from, MAX_PATH_LEN, None)
+ let mut visited_modules = FxHashSet::default();
+ find_path_inner(db, item, from, MAX_PATH_LEN, None, &mut visited_modules)
}
pub fn find_path_prefixed(
prefix_kind: PrefixKind,
) -> Option<ModPath> {
let _p = profile::span("find_path_prefixed");
- find_path_inner(db, item, from, MAX_PATH_LEN, Some(prefix_kind))
+ let mut visited_modules = FxHashSet::default();
+ find_path_inner(db, item, from, MAX_PATH_LEN, Some(prefix_kind), &mut visited_modules)
}
const MAX_PATH_LEN: usize = 15;
-impl ModPath {
+trait ModPathExt {
+ fn starts_with_std(&self) -> bool;
+ fn can_start_with_std(&self) -> bool;
+}
+
+impl ModPathExt for ModPath {
fn starts_with_std(&self) -> bool {
self.segments().first() == Some(&known::std)
}
from: ModuleId,
max_len: usize,
mut prefixed: Option<PrefixKind>,
+ visited_modules: &mut FxHashSet<ModuleId>,
) -> Option<ModPath> {
if max_len == 0 {
return None;
// - if the item is the crate root, return `crate`
let root = def_map.crate_root(db);
- if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() {
- // FIXME: the `block_id()` check should be unnecessary, but affects the result
+ if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) {
return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
}
}
// - if the item is the crate root of a dependency crate, return the name from the extern prelude
- for (name, def_id) in def_map.extern_prelude() {
+ let root_def_map = root.def_map(db);
+ for (name, def_id) in root_def_map.extern_prelude() {
if item == ItemInNs::Types(*def_id) {
let name = scope_name.unwrap_or_else(|| name.clone());
- return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
+
+ let name_already_occupied_in_type_ns = def_map
+ .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
+ def_map[local_id].scope.get(&name).take_types().filter(|&id| id != *def_id)
+ })
+ .is_some();
+ return Some(ModPath::from_segments(
+ if name_already_occupied_in_type_ns {
+ cov_mark::hit!(ambiguous_crate_start);
+ PathKind::Abs
+ } else {
+ PathKind::Plain
+ },
+ vec![name],
+ ));
}
}
// - if the item is in the prelude, return the name from there
- if let Some(prelude_module) = def_map.prelude() {
+ if let Some(prelude_module) = root_def_map.prelude() {
+ // Preludes in block DefMaps are ignored, only the crate DefMap is searched
let prelude_def_map = prelude_module.def_map(db);
let prelude_scope: &crate::item_scope::ItemScope =
&prelude_def_map[prelude_module.local_id].scope;
if item.krate(db) == Some(from.krate) {
// Item was defined in the same crate that wants to import it. It cannot be found in any
// dependency in this case.
-
- let local_imports = find_local_import_locations(db, item, from);
- for (module_id, name) in local_imports {
+ for (module_id, name) in find_local_import_locations(db, item, from) {
+ if !visited_modules.insert(module_id) {
+ cov_mark::hit!(recursive_imports);
+ continue;
+ }
if let Some(mut path) = find_path_inner(
db,
ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
from,
best_path_len - 1,
prefixed,
+ visited_modules,
) {
path.push_segment(name);
- let new_path = if let Some(best_path) = best_path {
- select_best_path(best_path, path, prefer_no_std)
- } else {
- path
+ let new_path = match best_path {
+ Some(best_path) => select_best_path(best_path, path, prefer_no_std),
+ None => path,
};
best_path_len = new_path.len();
best_path = Some(new_path);
from,
best_path_len - 1,
prefixed,
+ visited_modules,
)?;
cov_mark::hit!(partially_imported);
path.push_segment(info.path.segments.last().unwrap().clone());
});
for path in extern_paths {
- let new_path = if let Some(best_path) = best_path {
- select_best_path(best_path, path, prefer_no_std)
- } else {
- path
+ let new_path = match best_path {
+ Some(best_path) => select_best_path(best_path, path, prefer_no_std),
+ None => path,
};
best_path = Some(new_path);
}
}
}
- if let Some(prefix) = prefixed.map(PrefixKind::prefix) {
- best_path.or_else(|| {
+ match prefixed.map(PrefixKind::prefix) {
+ Some(prefix) => best_path.or_else(|| {
scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
- })
- } else {
- best_path
+ }),
+ None => best_path,
}
}
let data = &def_map[from.local_id];
let mut worklist =
data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>();
+ // FIXME: do we need to traverse out of block expressions here?
for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) {
worklist.push(ancestor);
}
if let Some((name, vis)) = data.scope.name_of(item) {
if vis.is_visible_from(db, from) {
- let is_private = if let Visibility::Module(private_to) = vis {
- private_to.local_id == module.local_id
- } else {
- false
+ let is_private = match vis {
+ Visibility::Module(private_to) => private_to.local_id == module.local_id,
+ Visibility::Public => false,
};
- let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
- data.scope.declarations().any(|it| it == module_def_id)
- } else {
- false
+ let is_original_def = match item.as_module_def_id() {
+ Some(module_def_id) => data.scope.declarations().any(|it| it == module_def_id),
+ None => false,
};
// Ignore private imports. these could be used if we are
let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
let ast_path =
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
- let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
+ let mod_path = ModPath::from_src(&db, ast_path, &Hygiene::new_unhygienic()).unwrap();
let def_map = module.def_map(&db);
let resolved = def_map
.take_types()
.unwrap();
- let found_path =
- find_path_inner(&db, ItemInNs::Types(resolved), module, MAX_PATH_LEN, prefix_kind);
+ let mut visited_modules = FxHashSet::default();
+ let found_path = find_path_inner(
+ &db,
+ ItemInNs::Types(resolved),
+ module,
+ MAX_PATH_LEN,
+ prefix_kind,
+ &mut visited_modules,
+ );
assert_eq!(found_path, Some(mod_path), "{:?}", prefix_kind);
}
#[test]
fn same_module() {
- let code = r#"
- //- /main.rs
- struct S;
- $0
- "#;
- check_found_path(code, "S", "S", "crate::S", "self::S");
+ check_found_path(
+ r#"
+struct S;
+$0
+ "#,
+ "S",
+ "S",
+ "crate::S",
+ "self::S",
+ );
}
#[test]
fn enum_variant() {
- let code = r#"
- //- /main.rs
- enum E { A }
- $0
- "#;
- check_found_path(code, "E::A", "E::A", "E::A", "E::A");
+ check_found_path(
+ r#"
+enum E { A }
+$0
+ "#,
+ "E::A",
+ "E::A",
+ "E::A",
+ "E::A",
+ );
}
#[test]
fn sub_module() {
- let code = r#"
- //- /main.rs
- mod foo {
- pub struct S;
- }
- $0
- "#;
- check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S");
+ check_found_path(
+ r#"
+mod foo {
+ pub struct S;
+}
+$0
+ "#,
+ "foo::S",
+ "foo::S",
+ "crate::foo::S",
+ "self::foo::S",
+ );
}
#[test]
fn super_module() {
- let code = r#"
- //- /main.rs
- mod foo;
- //- /foo.rs
- mod bar;
- struct S;
- //- /foo/bar.rs
- $0
- "#;
- check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S");
+ check_found_path(
+ r#"
+//- /main.rs
+mod foo;
+//- /foo.rs
+mod bar;
+struct S;
+//- /foo/bar.rs
+$0
+ "#,
+ "super::S",
+ "super::S",
+ "crate::foo::S",
+ "super::S",
+ );
}
#[test]
fn self_module() {
- let code = r#"
- //- /main.rs
- mod foo;
- //- /foo.rs
- $0
- "#;
- check_found_path(code, "self", "self", "crate::foo", "self");
+ check_found_path(
+ r#"
+//- /main.rs
+mod foo;
+//- /foo.rs
+$0
+ "#,
+ "self",
+ "self",
+ "crate::foo",
+ "self",
+ );
}
#[test]
fn crate_root() {
- let code = r#"
- //- /main.rs
- mod foo;
- //- /foo.rs
- $0
- "#;
- check_found_path(code, "crate", "crate", "crate", "crate");
+ check_found_path(
+ r#"
+//- /main.rs
+mod foo;
+//- /foo.rs
+$0
+ "#,
+ "crate",
+ "crate",
+ "crate",
+ "crate",
+ );
}
#[test]
fn same_crate() {
- let code = r#"
- //- /main.rs
- mod foo;
- struct S;
- //- /foo.rs
- $0
- "#;
- check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S");
+ check_found_path(
+ r#"
+//- /main.rs
+mod foo;
+struct S;
+//- /foo.rs
+$0
+ "#,
+ "crate::S",
+ "crate::S",
+ "crate::S",
+ "crate::S",
+ );
}
#[test]
fn different_crate() {
- let code = r#"
- //- /main.rs crate:main deps:std
- $0
- //- /std.rs crate:std
- pub struct S;
- "#;
- check_found_path(code, "std::S", "std::S", "std::S", "std::S");
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:std
+$0
+//- /std.rs crate:std
+pub struct S;
+ "#,
+ "std::S",
+ "std::S",
+ "std::S",
+ "std::S",
+ );
}
#[test]
fn different_crate_renamed() {
- let code = r#"
- //- /main.rs crate:main deps:std
- extern crate std as std_renamed;
- $0
- //- /std.rs crate:std
- pub struct S;
- "#;
check_found_path(
- code,
+ r#"
+//- /main.rs crate:main deps:std
+extern crate std as std_renamed;
+$0
+//- /std.rs crate:std
+pub struct S;
+ "#,
"std_renamed::S",
"std_renamed::S",
"std_renamed::S",
cov_mark::check!(partially_imported);
// Tests that short paths are used even for external items, when parts of the path are
// already in scope.
- let code = r#"
- //- /main.rs crate:main deps:syntax
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:syntax
- use syntax::ast;
- $0
+use syntax::ast;
+$0
- //- /lib.rs crate:syntax
- pub mod ast {
- pub enum ModuleItem {
- A, B, C,
- }
- }
- "#;
- check_found_path(
- code,
+//- /lib.rs crate:syntax
+pub mod ast {
+ pub enum ModuleItem {
+ A, B, C,
+ }
+}
+ "#,
"ast::ModuleItem",
"syntax::ast::ModuleItem",
"syntax::ast::ModuleItem",
"syntax::ast::ModuleItem",
);
- let code = r#"
- //- /main.rs crate:main deps:syntax
-
- $0
-
- //- /lib.rs crate:syntax
- pub mod ast {
- pub enum ModuleItem {
- A, B, C,
- }
- }
- "#;
check_found_path(
- code,
+ r#"
+//- /main.rs crate:main deps:syntax
+$0
+
+//- /lib.rs crate:syntax
+pub mod ast {
+ pub enum ModuleItem {
+ A, B, C,
+ }
+}
+ "#,
"syntax::ast::ModuleItem",
"syntax::ast::ModuleItem",
"syntax::ast::ModuleItem",
#[test]
fn same_crate_reexport() {
- let code = r#"
- //- /main.rs
- mod bar {
- mod foo { pub(super) struct S; }
- pub(crate) use foo::*;
- }
- $0
- "#;
- check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S");
+ check_found_path(
+ r#"
+mod bar {
+ mod foo { pub(super) struct S; }
+ pub(crate) use foo::*;
+}
+$0
+ "#,
+ "bar::S",
+ "bar::S",
+ "crate::bar::S",
+ "self::bar::S",
+ );
}
#[test]
fn same_crate_reexport_rename() {
- let code = r#"
- //- /main.rs
- mod bar {
- mod foo { pub(super) struct S; }
- pub(crate) use foo::S as U;
- }
- $0
- "#;
- check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U");
+ check_found_path(
+ r#"
+mod bar {
+ mod foo { pub(super) struct S; }
+ pub(crate) use foo::S as U;
+}
+$0
+ "#,
+ "bar::U",
+ "bar::U",
+ "crate::bar::U",
+ "self::bar::U",
+ );
}
#[test]
fn different_crate_reexport() {
- let code = r#"
- //- /main.rs crate:main deps:std
- $0
- //- /std.rs crate:std deps:core
- pub use core::S;
- //- /core.rs crate:core
- pub struct S;
- "#;
- check_found_path(code, "std::S", "std::S", "std::S", "std::S");
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:std
+$0
+//- /std.rs crate:std deps:core
+pub use core::S;
+//- /core.rs crate:core
+pub struct S;
+ "#,
+ "std::S",
+ "std::S",
+ "std::S",
+ "std::S",
+ );
}
#[test]
fn prelude() {
- let code = r#"
- //- /main.rs crate:main deps:std
- $0
- //- /std.rs crate:std
- pub mod prelude { pub struct S; }
- #[prelude_import]
- pub use prelude::*;
- "#;
- check_found_path(code, "S", "S", "S", "S");
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:std
+$0
+//- /std.rs crate:std
+pub mod prelude {
+ pub mod rust_2018 {
+ pub struct S;
+ }
+}
+ "#,
+ "S",
+ "S",
+ "S",
+ "S",
+ );
}
#[test]
fn enum_variant_from_prelude() {
let code = r#"
- //- /main.rs crate:main deps:std
- $0
- //- /std.rs crate:std
- pub mod prelude {
- pub enum Option<T> { Some(T), None }
- pub use Option::*;
- }
- #[prelude_import]
- pub use prelude::*;
+//- /main.rs crate:main deps:std
+$0
+//- /std.rs crate:std
+pub mod prelude {
+ pub mod rust_2018 {
+ pub enum Option<T> { Some(T), None }
+ pub use Option::*;
+ }
+}
"#;
check_found_path(code, "None", "None", "None", "None");
check_found_path(code, "Some", "Some", "Some", "Some");
#[test]
fn shortest_path() {
- let code = r#"
- //- /main.rs
- pub mod foo;
- pub mod baz;
- struct S;
- $0
- //- /foo.rs
- pub mod bar { pub struct S; }
- //- /baz.rs
- pub use crate::foo::bar::S;
- "#;
- check_found_path(code, "baz::S", "baz::S", "crate::baz::S", "self::baz::S");
+ check_found_path(
+ r#"
+//- /main.rs
+pub mod foo;
+pub mod baz;
+struct S;
+$0
+//- /foo.rs
+pub mod bar { pub struct S; }
+//- /baz.rs
+pub use crate::foo::bar::S;
+ "#,
+ "baz::S",
+ "baz::S",
+ "crate::baz::S",
+ "self::baz::S",
+ );
}
#[test]
fn discount_private_imports() {
- let code = r#"
- //- /main.rs
- mod foo;
- pub mod bar { pub struct S; }
- use bar::S;
- //- /foo.rs
- $0
- "#;
- // crate::S would be shorter, but using private imports seems wrong
- check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S");
+ check_found_path(
+ r#"
+//- /main.rs
+mod foo;
+pub mod bar { pub struct S; }
+use bar::S;
+//- /foo.rs
+$0
+ "#,
+ // crate::S would be shorter, but using private imports seems wrong
+ "crate::bar::S",
+ "crate::bar::S",
+ "crate::bar::S",
+ "crate::bar::S",
+ );
}
#[test]
fn import_cycle() {
- let code = r#"
- //- /main.rs
- pub mod foo;
- pub mod bar;
- pub mod baz;
- //- /bar.rs
- $0
- //- /foo.rs
- pub use super::baz;
- pub struct S;
- //- /baz.rs
- pub use super::foo;
- "#;
- check_found_path(code, "crate::foo::S", "crate::foo::S", "crate::foo::S", "crate::foo::S");
+ check_found_path(
+ r#"
+//- /main.rs
+pub mod foo;
+pub mod bar;
+pub mod baz;
+//- /bar.rs
+$0
+//- /foo.rs
+pub use super::baz;
+pub struct S;
+//- /baz.rs
+pub use super::foo;
+ "#,
+ "crate::foo::S",
+ "crate::foo::S",
+ "crate::foo::S",
+ "crate::foo::S",
+ );
}
#[test]
fn prefer_std_paths_over_alloc() {
cov_mark::check!(prefer_std_paths);
- let code = r#"
- //- /main.rs crate:main deps:alloc,std
- $0
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:alloc,std
+$0
- //- /std.rs crate:std deps:alloc
- pub mod sync {
- pub use alloc::sync::Arc;
- }
+//- /std.rs crate:std deps:alloc
+pub mod sync {
+ pub use alloc::sync::Arc;
+}
- //- /zzz.rs crate:alloc
- pub mod sync {
- pub struct Arc;
- }
- "#;
- check_found_path(
- code,
+//- /zzz.rs crate:alloc
+pub mod sync {
+ pub struct Arc;
+}
+ "#,
"std::sync::Arc",
"std::sync::Arc",
"std::sync::Arc",
#[test]
fn prefer_core_paths_over_std() {
cov_mark::check!(prefer_no_std_paths);
- let code = r#"
- //- /main.rs crate:main deps:core,std
- #![no_std]
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:core,std
+#![no_std]
- $0
+$0
- //- /std.rs crate:std deps:core
+//- /std.rs crate:std deps:core
- pub mod fmt {
- pub use core::fmt::Error;
- }
+pub mod fmt {
+ pub use core::fmt::Error;
+}
- //- /zzz.rs crate:core
+//- /zzz.rs crate:core
- pub mod fmt {
- pub struct Error;
- }
- "#;
- check_found_path(
- code,
+pub mod fmt {
+ pub struct Error;
+}
+ "#,
"core::fmt::Error",
"core::fmt::Error",
"core::fmt::Error",
#[test]
fn prefer_alloc_paths_over_std() {
- let code = r#"
- //- /main.rs crate:main deps:alloc,std
- #![no_std]
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:alloc,std
+#![no_std]
- $0
+$0
- //- /std.rs crate:std deps:alloc
+//- /std.rs crate:std deps:alloc
- pub mod sync {
- pub use alloc::sync::Arc;
- }
+pub mod sync {
+ pub use alloc::sync::Arc;
+}
- //- /zzz.rs crate:alloc
+//- /zzz.rs crate:alloc
- pub mod sync {
- pub struct Arc;
- }
- "#;
- check_found_path(
- code,
+pub mod sync {
+ pub struct Arc;
+}
+ "#,
"alloc::sync::Arc",
"alloc::sync::Arc",
"alloc::sync::Arc",
#[test]
fn prefer_shorter_paths_if_not_alloc() {
- let code = r#"
- //- /main.rs crate:main deps:megaalloc,std
- $0
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:megaalloc,std
+$0
- //- /std.rs crate:std deps:megaalloc
- pub mod sync {
- pub use megaalloc::sync::Arc;
- }
+//- /std.rs crate:std deps:megaalloc
+pub mod sync {
+ pub use megaalloc::sync::Arc;
+}
- //- /zzz.rs crate:megaalloc
- pub struct Arc;
- "#;
- check_found_path(
- code,
+//- /zzz.rs crate:megaalloc
+pub struct Arc;
+ "#,
"megaalloc::Arc",
"megaalloc::Arc",
"megaalloc::Arc",
#[test]
fn builtins_are_in_scope() {
let code = r#"
- //- /main.rs
- $0
+$0
- pub mod primitive {
- pub use u8;
- }
+pub mod primitive {
+ pub use u8;
+}
"#;
check_found_path(code, "u8", "u8", "u8", "u8");
check_found_path(code, "u16", "u16", "u16", "u16");
fn inner_items() {
check_found_path(
r#"
- fn main() {
- struct Inner {}
- $0
- }
+fn main() {
+ struct Inner {}
+ $0
+}
"#,
"Inner",
"Inner",
fn inner_items_from_outer_scope() {
check_found_path(
r#"
- fn main() {
- struct Struct {}
- {
- $0
- }
- }
+fn main() {
+ struct Struct {}
+ {
+ $0
+ }
+}
"#,
"Struct",
"Struct",
cov_mark::check!(prefixed_in_block_expression);
check_found_path(
r#"
- fn main() {
- mod module {
- struct Struct {}
- }
- {
- $0
- }
- }
+fn main() {
+ mod module {
+ struct Struct {}
+ }
+ {
+ $0
+ }
+}
"#,
"module::Struct",
"module::Struct",
fn outer_items_with_inner_items_present() {
check_found_path(
r#"
- mod module {
- pub struct CompleteMe;
- }
+mod module {
+ pub struct CompleteMe;
+}
- fn main() {
- fn inner() {}
- $0
- }
+fn main() {
+ fn inner() {}
+ $0
+}
"#,
+ // FIXME: these could use fewer/better prefixes
"module::CompleteMe",
- "module::CompleteMe",
"crate::module::CompleteMe",
- "self::module::CompleteMe",
+ "crate::module::CompleteMe",
+ "crate::module::CompleteMe",
)
}
+
+ #[test]
+ fn from_inside_module() {
+ // This worked correctly, but the test suite logic was broken.
+ cov_mark::check!(submodule_in_testdb);
+ check_found_path(
+ r#"
+mod baz {
+ pub struct Foo {}
+}
+
+mod bar {
+ fn bar() {
+ $0
+ }
+}
+ "#,
+ "crate::baz::Foo",
+ "crate::baz::Foo",
+ "crate::baz::Foo",
+ "crate::baz::Foo",
+ )
+ }
+
+ #[test]
+ fn from_inside_module_with_inner_items() {
+ check_found_path(
+ r#"
+mod baz {
+ pub struct Foo {}
+}
+
+mod bar {
+ fn bar() {
+ fn inner() {}
+ $0
+ }
+}
+ "#,
+ "crate::baz::Foo",
+ "crate::baz::Foo",
+ "crate::baz::Foo",
+ "crate::baz::Foo",
+ )
+ }
+
+ #[test]
+ fn recursive_pub_mod_reexport() {
+ cov_mark::check!(recursive_imports);
+ check_found_path(
+ r#"
+fn main() {
+ let _ = 22_i32.as_name$0();
+}
+
+pub mod name {
+ pub trait AsName {
+ fn as_name(&self) -> String;
+ }
+ impl AsName for i32 {
+ fn as_name(&self) -> String {
+ format!("Name: {}", self)
+ }
+ }
+ pub use crate::name;
+}
+"#,
+ "name::AsName",
+ "name::AsName",
+ "crate::name::AsName",
+ "self::name::AsName",
+ );
+ }
+
+ #[test]
+ fn extern_crate() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:dep
+$0
+//- /dep.rs crate:dep
+"#,
+ "dep",
+ "dep",
+ "dep",
+ "dep",
+ );
+
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:dep
+fn f() {
+ fn inner() {}
+ $0
+}
+//- /dep.rs crate:dep
+"#,
+ "dep",
+ "dep",
+ "dep",
+ "dep",
+ );
+ }
+
+ #[test]
+ fn prelude_with_inner_items() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:std
+fn f() {
+ fn inner() {}
+ $0
+}
+//- /std.rs crate:std
+pub mod prelude {
+ pub mod rust_2018 {
+ pub enum Option { None }
+ pub use Option::*;
+ }
+}
+ "#,
+ "None",
+ "None",
+ "None",
+ "None",
+ );
+ }
}