]> git.lizzy.rs Git - rust.git/blobdiff - crates/hir_def/src/find_path.rs
parameters.split_last()
[rust.git] / crates / hir_def / src / find_path.rs
index 41da3bc2d42539e0cb44ac577bdad06799e6c5cf..22d593a7d97b46411c31d78f62a4037de5998293 100644 (file)
@@ -5,10 +5,10 @@
 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,
@@ -35,7 +35,12 @@ pub fn find_path_prefixed(
 
 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)
     }
@@ -119,8 +124,7 @@ fn find_path_inner(
 
     // - 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()));
     }
 
@@ -131,15 +135,31 @@ fn find_path_inner(
     }
 
     // - 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;
@@ -194,10 +214,9 @@ fn find_path_inner(
             ) {
                 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);
@@ -228,10 +247,9 @@ fn find_path_inner(
         });
 
         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);
         }
@@ -246,12 +264,11 @@ fn find_path_inner(
         }
     }
 
-    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,
     }
 }
 
@@ -298,6 +315,7 @@ fn find_local_import_locations(
     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);
     }
@@ -330,15 +348,13 @@ fn find_local_import_locations(
 
         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
@@ -384,7 +400,7 @@ fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKin
         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
@@ -671,9 +687,11 @@ fn prelude() {
 //- /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",
@@ -689,11 +707,11 @@ fn enum_variant_from_prelude() {
 $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");
@@ -947,10 +965,56 @@ 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",
         )
     }
 
@@ -981,4 +1045,58 @@ fn as_name(&self) -> String {
             "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",
+        );
+    }
 }