aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-02-09 16:25:03 +0000
committerJonas Schievink <[email protected]>2021-02-09 16:25:03 +0000
commit34ad3d629a75c8bbc419c0159c213901ce8ad28d (patch)
treef458a6a05267fca4d60833a914b6d2ce93944389 /crates/hir_def
parent7067a22e1c1ab9041475359922d1863f7d9a4f5c (diff)
Teach `find_path` about inner items
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/src/find_path.rs101
1 files changed, 91 insertions, 10 deletions
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index aa2c6e04e..5e2a711b8 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -13,8 +13,6 @@ use crate::{
13 ModuleDefId, ModuleId, 13 ModuleDefId, ModuleId,
14}; 14};
15 15
16// FIXME: handle local items
17
18/// Find a path that can be used to refer to a certain item. This can depend on 16/// Find a path that can be used to refer to a certain item. This can depend on
19/// *from where* you're referring to the item, hence the `from` parameter. 17/// *from where* you're referring to the item, hence the `from` parameter.
20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 18pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
@@ -107,9 +105,9 @@ fn find_path_inner(
107 105
108 // - if the item is already in scope, return the name under which it is 106 // - if the item is already in scope, return the name under which it is
109 let def_map = from.def_map(db); 107 let def_map = from.def_map(db);
110 let from_scope: &crate::item_scope::ItemScope = &def_map[from.local_id].scope; 108 let scope_name = def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
111 let scope_name = 109 def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone())
112 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None }; 110 });
113 if prefixed.is_none() && scope_name.is_some() { 111 if prefixed.is_none() && scope_name.is_some() {
114 return scope_name 112 return scope_name
115 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name])); 113 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name]));
@@ -117,7 +115,7 @@ fn find_path_inner(
117 115
118 // - if the item is the crate root, return `crate` 116 // - if the item is the crate root, return `crate`
119 let root = def_map.module_id(def_map.root()); 117 let root = def_map.module_id(def_map.root());
120 if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) { 118 if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() {
121 return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); 119 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
122 } 120 }
123 121
@@ -230,7 +228,12 @@ fn find_path_inner(
230 } 228 }
231 } 229 }
232 230
233 if let Some(prefix) = prefixed.map(PrefixKind::prefix) { 231 if let Some(mut prefix) = prefixed.map(PrefixKind::prefix) {
232 if matches!(prefix, PathKind::Crate | PathKind::Super(0)) && def_map.block_id().is_some() {
233 // Inner items cannot be referred to via `crate::` or `self::` paths.
234 prefix = PathKind::Plain;
235 }
236
234 best_path.or_else(|| { 237 best_path.or_else(|| {
235 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name])) 238 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
236 }) 239 })
@@ -358,14 +361,14 @@ mod tests {
358 /// module the cursor is in. 361 /// module the cursor is in.
359 fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) { 362 fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) {
360 let (db, pos) = TestDB::with_position(ra_fixture); 363 let (db, pos) = TestDB::with_position(ra_fixture);
361 let module = db.module_for_file(pos.file_id); 364 let module = db.module_at_position(pos);
362 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); 365 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
363 let ast_path = 366 let ast_path =
364 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); 367 parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
365 let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap(); 368 let mod_path = ModPath::from_src(ast_path, &Hygiene::new_unhygienic()).unwrap();
366 369
367 let crate_def_map = module.def_map(&db); 370 let def_map = module.def_map(&db);
368 let resolved = crate_def_map 371 let resolved = def_map
369 .resolve_path( 372 .resolve_path(
370 &db, 373 &db,
371 module.local_id, 374 module.local_id,
@@ -788,4 +791,82 @@ mod tests {
788 check_found_path(code, "u8", "u8", "u8", "u8"); 791 check_found_path(code, "u8", "u8", "u8", "u8");
789 check_found_path(code, "u16", "u16", "u16", "u16"); 792 check_found_path(code, "u16", "u16", "u16", "u16");
790 } 793 }
794
795 #[test]
796 fn inner_items() {
797 check_found_path(
798 r#"
799 fn main() {
800 struct Inner {}
801 $0
802 }
803 "#,
804 "Inner",
805 "Inner",
806 "Inner",
807 "Inner",
808 );
809 }
810
811 #[test]
812 fn inner_items_from_outer_scope() {
813 check_found_path(
814 r#"
815 fn main() {
816 struct Struct {}
817 {
818 $0
819 }
820 }
821 "#,
822 "Struct",
823 "Struct",
824 "Struct",
825 "Struct",
826 );
827 }
828
829 #[test]
830 fn inner_items_from_inner_module() {
831 check_found_path(
832 r#"
833 fn main() {
834 mod module {
835 struct Struct {}
836 }
837 {
838 $0
839 }
840 }
841 "#,
842 "module::Struct",
843 "module::Struct",
844 "module::Struct",
845 "module::Struct",
846 );
847 }
848
849 #[test]
850 #[ignore]
851 fn inner_items_from_parent_module() {
852 // FIXME: ItemTree currently associates all inner items with `main`. Luckily, this sort of
853 // code is very rare, so this isn't terrible.
854 // To fix it, we should probably build dedicated `ItemTree`s for inner items, and not store
855 // them in the file's main ItemTree. This would also allow us to stop parsing function
856 // bodies when we only want to compute the crate's main DefMap.
857 check_found_path(
858 r#"
859 fn main() {
860 struct Struct {}
861 mod module {
862 $0
863 }
864 }
865 "#,
866 "super::Struct",
867 "super::Struct",
868 "super::Struct",
869 "super::Struct",
870 );
871 }
791} 872}