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,
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)
}
// - 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;
) {
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);
});
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
//- /main.rs crate:main deps:std
$0
//- /std.rs crate:std
-pub mod prelude { pub struct S; }
-#[prelude_import]
-pub use prelude::*;
+pub mod prelude {
+ pub mod rust_2018 {
+ pub struct S;
+ }
+}
"#,
"S",
"S",
$0
//- /std.rs crate:std
pub mod prelude {
- pub enum Option<T> { Some(T), None }
- pub use Option::*;
+ pub mod rust_2018 {
+ pub enum Option<T> { Some(T), None }
+ pub use Option::*;
+ }
}
-#[prelude_import]
-pub use prelude::*;
"#;
check_found_path(code, "None", "None", "None", "None");
check_found_path(code, "Some", "Some", "Some", "Some");
$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_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);
"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",
+ );
+ }
}