]> 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 5e2a711b84a5b6a6e5905685d0ef68f36b7d5241..22d593a7d97b46411c31d78f62a4037de5998293 100644 (file)
@@ -1,13 +1,14 @@
 //! An algorithm to find a path to refer to a certain item.
 
+use std::iter;
+
 use hir_expand::name::{known, AsName, Name};
 use rustc_hash::FxHashSet;
-use test_utils::mark;
 
-use crate::nameres::DefMap;
 use crate::{
     db::DefDatabase,
     item_scope::ItemInNs,
+    nameres::DefMap,
     path::{ModPath, PathKind},
     visibility::Visibility,
     ModuleDefId, ModuleId,
@@ -17,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(
@@ -27,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)
     }
@@ -95,7 +103,8 @@ fn find_path_inner(
     item: ItemInNs,
     from: ModuleId,
     max_len: usize,
-    prefixed: Option<PrefixKind>,
+    mut prefixed: Option<PrefixKind>,
+    visited_modules: &mut FxHashSet<ModuleId>,
 ) -> Option<ModPath> {
     if max_len == 0 {
         return None;
@@ -114,8 +123,8 @@ fn find_path_inner(
     }
 
     // - if the item is the crate root, return `crate`
-    let root = def_map.module_id(def_map.root());
-    if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() {
+    let root = def_map.crate_root(db);
+    if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) {
         return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
     }
 
@@ -126,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;
@@ -165,7 +190,7 @@ fn find_path_inner(
 
     // - otherwise, look for modules containing (reexporting) it and import it from one of those
 
-    let crate_root = def_map.module_id(def_map.root());
+    let crate_root = def_map.crate_root(db);
     let crate_attrs = db.attrs(crate_root.into());
     let prefer_no_std = crate_attrs.by_key("no_std").exists();
     let mut best_path = None;
@@ -174,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);
@@ -211,52 +238,55 @@ fn find_path_inner(
                     from,
                     best_path_len - 1,
                     prefixed,
+                    visited_modules,
                 )?;
-                mark::hit!(partially_imported);
+                cov_mark::hit!(partially_imported);
                 path.push_segment(info.path.segments.last().unwrap().clone());
                 Some(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(mut prefix) = prefixed.map(PrefixKind::prefix) {
-        if matches!(prefix, PathKind::Crate | PathKind::Super(0)) && def_map.block_id().is_some() {
-            // Inner items cannot be referred to via `crate::` or `self::` paths.
-            prefix = PathKind::Plain;
+    // If the item is declared inside a block expression, don't use a prefix, as we don't handle
+    // that correctly (FIXME).
+    if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) {
+        if item_module.def_map(db).block_id().is_some() && prefixed.is_some() {
+            cov_mark::hit!(prefixed_in_block_expression);
+            prefixed = Some(PrefixKind::Plain);
         }
+    }
 
-        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,
     }
 }
 
 fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
     if old_path.starts_with_std() && new_path.can_start_with_std() {
         if prefer_no_std {
-            mark::hit!(prefer_no_std_paths);
+            cov_mark::hit!(prefer_no_std_paths);
             new_path
         } else {
-            mark::hit!(prefer_std_paths);
+            cov_mark::hit!(prefer_std_paths);
             old_path
         }
     } else if new_path.starts_with_std() && old_path.can_start_with_std() {
         if prefer_no_std {
-            mark::hit!(prefer_no_std_paths);
+            cov_mark::hit!(prefer_no_std_paths);
             old_path
         } else {
-            mark::hit!(prefer_std_paths);
+            cov_mark::hit!(prefer_std_paths);
             new_path
         }
     } else if new_path.len() < old_path.len() {
@@ -285,12 +315,13 @@ 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<_>>();
-    let mut parent = data.parent;
-    while let Some(p) = parent {
-        worklist.push(def_map.module_id(p));
-        parent = def_map[p].parent;
+    // 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);
     }
 
+    let def_map = def_map.crate_root(db).def_map(db);
+
     let mut seen: FxHashSet<_> = FxHashSet::default();
 
     let mut locations = Vec::new();
@@ -301,7 +332,14 @@ fn find_local_import_locations(
 
         let ext_def_map;
         let data = if module.krate == from.krate {
-            &def_map[module.local_id]
+            if module.block.is_some() {
+                // Re-query the block's DefMap
+                ext_def_map = module.def_map(db);
+                &ext_def_map[module.local_id]
+            } else {
+                // Reuse the root DefMap
+                &def_map[module.local_id]
+            }
         } else {
             // The crate might reexport a module defined in another crate.
             ext_def_map = module.def_map(db);
@@ -310,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
@@ -350,7 +386,6 @@ mod tests {
     use base_db::fixture::WithFixture;
     use hir_expand::hygiene::Hygiene;
     use syntax::ast::AstNode;
-    use test_utils::mark;
 
     use crate::test_db::TestDB;
 
@@ -365,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
@@ -379,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);
     }
 
@@ -399,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",
@@ -508,44 +586,41 @@ fn different_crate_renamed() {
 
     #[test]
     fn partially_imported() {
-        mark::check!(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",
@@ -555,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");
@@ -624,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() {
-        mark::check!(prefer_std_paths);
-        let code = r#"
-        //- /main.rs crate:main deps:alloc,std
-        $0
+        cov_mark::check!(prefer_std_paths);
+        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",
@@ -698,27 +807,26 @@ pub mod sync {
 
     #[test]
     fn prefer_core_paths_over_std() {
-        mark::check!(prefer_no_std_paths);
-        let code = r#"
-        //- /main.rs crate:main deps:core,std
-        #![no_std]
+        cov_mark::check!(prefer_no_std_paths);
+        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",
@@ -728,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",
@@ -757,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",
@@ -781,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");
@@ -796,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",
@@ -812,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",
@@ -828,16 +933,17 @@ struct Struct {}
 
     #[test]
     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",
@@ -847,26 +953,150 @@ struct Struct {}
     }
 
     #[test]
-    #[ignore]
-    fn inner_items_from_parent_module() {
-        // FIXME: ItemTree currently associates all inner items with `main`. Luckily, this sort of
-        // code is very rare, so this isn't terrible.
-        // To fix it, we should probably build dedicated `ItemTree`s for inner items, and not store
-        // them in the file's main ItemTree. This would also allow us to stop parsing function
-        // bodies when we only want to compute the crate's main DefMap.
+    fn outer_items_with_inner_items_present() {
         check_found_path(
             r#"
-            fn main() {
-                struct Struct {}
-                mod module {
-                    $0
-                }
-            }
+mod module {
+    pub struct CompleteMe;
+}
+
+fn main() {
+    fn inner() {}
+    $0
+}
+            "#,
+            // FIXME: these could use fewer/better prefixes
+            "module::CompleteMe",
+            "crate::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::*;
+    }
+}
         "#,
-            "super::Struct",
-            "super::Struct",
-            "super::Struct",
-            "super::Struct",
+            "None",
+            "None",
+            "None",
+            "None",
         );
     }
 }