]> 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 de08e2737a261752b8355e6ded675b3cc99d8952..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,
@@ -18,7 +18,8 @@
 /// *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(
@@ -28,12 +29,18 @@ 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)
     }
@@ -97,6 +104,7 @@ fn find_path_inner(
     from: ModuleId,
     max_len: usize,
     mut prefixed: Option<PrefixKind>,
+    visited_modules: &mut FxHashSet<ModuleId>,
 ) -> Option<ModPath> {
     if max_len == 0 {
         return None;
@@ -116,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()));
     }
 
@@ -128,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;
@@ -176,22 +199,24 @@ fn find_path_inner(
     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);
@@ -213,6 +238,7 @@ fn find_path_inner(
                     from,
                     best_path_len - 1,
                     prefixed,
+                    visited_modules,
                 )?;
                 cov_mark::hit!(partially_imported);
                 path.push_segment(info.path.segments.last().unwrap().clone());
@@ -221,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);
         }
@@ -239,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,
     }
 }
 
@@ -291,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);
     }
@@ -323,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
@@ -377,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
@@ -391,8 +414,15 @@ fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKin
             .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);
     }
 
@@ -411,106 +441,142 @@ fn check_found_path(
 
     #[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",
@@ -523,41 +589,38 @@ fn partially_imported() {
         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",
@@ -567,68 +630,88 @@ pub enum 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");
@@ -636,71 +719,85 @@ pub enum Option<T> { Some(T), None }
 
     #[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",
@@ -711,26 +808,25 @@ pub mod sync {
     #[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",
@@ -740,26 +836,25 @@ pub mod fmt {
 
     #[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",
@@ -769,20 +864,19 @@ pub mod sync {
 
     #[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",
@@ -793,12 +887,11 @@ pub mod sync {
     #[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");
@@ -808,10 +901,10 @@ pub mod primitive {
     fn inner_items() {
         check_found_path(
             r#"
-            fn main() {
-                struct Inner {}
-                $0
-            }
+fn main() {
+    struct Inner {}
+    $0
+}
         "#,
             "Inner",
             "Inner",
@@ -824,12 +917,12 @@ struct Inner {}
     fn inner_items_from_outer_scope() {
         check_found_path(
             r#"
-            fn main() {
-                struct Struct {}
-                {
-                    $0
-                }
-            }
+fn main() {
+    struct Struct {}
+    {
+        $0
+    }
+}
         "#,
             "Struct",
             "Struct",
@@ -843,14 +936,14 @@ fn inner_items_from_inner_module() {
         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",
@@ -863,19 +956,147 @@ struct 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",
+        );
+    }
 }