diff options
-rw-r--r-- | crates/hir_def/src/body/scope.rs | 22 | ||||
-rw-r--r-- | crates/hir_def/src/body/tests.rs | 104 | ||||
-rw-r--r-- | crates/hir_def/src/body/tests/block.rs | 27 | ||||
-rw-r--r-- | crates/hir_def/src/find_path.rs | 101 | ||||
-rw-r--r-- | crates/hir_def/src/nameres.rs | 2 | ||||
-rw-r--r-- | crates/hir_def/src/resolver.rs | 215 | ||||
-rw-r--r-- | crates/hir_def/src/test_db.rs | 99 |
7 files changed, 333 insertions, 237 deletions
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs index 49f1427b4..210b4a617 100644 --- a/crates/hir_def/src/body/scope.rs +++ b/crates/hir_def/src/body/scope.rs | |||
@@ -9,7 +9,7 @@ use crate::{ | |||
9 | body::Body, | 9 | body::Body, |
10 | db::DefDatabase, | 10 | db::DefDatabase, |
11 | expr::{Expr, ExprId, Pat, PatId, Statement}, | 11 | expr::{Expr, ExprId, Pat, PatId, Statement}, |
12 | DefWithBodyId, | 12 | BlockId, DefWithBodyId, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | pub type ScopeId = Idx<ScopeData>; | 15 | pub type ScopeId = Idx<ScopeData>; |
@@ -39,6 +39,7 @@ impl ScopeEntry { | |||
39 | #[derive(Debug, PartialEq, Eq)] | 39 | #[derive(Debug, PartialEq, Eq)] |
40 | pub struct ScopeData { | 40 | pub struct ScopeData { |
41 | parent: Option<ScopeId>, | 41 | parent: Option<ScopeId>, |
42 | block: Option<BlockId>, | ||
42 | entries: Vec<ScopeEntry>, | 43 | entries: Vec<ScopeEntry>, |
43 | } | 44 | } |
44 | 45 | ||
@@ -61,6 +62,11 @@ impl ExprScopes { | |||
61 | &self.scopes[scope].entries | 62 | &self.scopes[scope].entries |
62 | } | 63 | } |
63 | 64 | ||
65 | /// If `scope` refers to a block expression scope, returns the corresponding `BlockId`. | ||
66 | pub fn block(&self, scope: ScopeId) -> Option<BlockId> { | ||
67 | self.scopes[scope].block | ||
68 | } | ||
69 | |||
64 | pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ { | 70 | pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ { |
65 | std::iter::successors(scope, move |&scope| self.scopes[scope].parent) | 71 | std::iter::successors(scope, move |&scope| self.scopes[scope].parent) |
66 | } | 72 | } |
@@ -79,11 +85,15 @@ impl ExprScopes { | |||
79 | } | 85 | } |
80 | 86 | ||
81 | fn root_scope(&mut self) -> ScopeId { | 87 | fn root_scope(&mut self) -> ScopeId { |
82 | self.scopes.alloc(ScopeData { parent: None, entries: vec![] }) | 88 | self.scopes.alloc(ScopeData { parent: None, block: None, entries: vec![] }) |
83 | } | 89 | } |
84 | 90 | ||
85 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | 91 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { |
86 | self.scopes.alloc(ScopeData { parent: Some(parent), entries: vec![] }) | 92 | self.scopes.alloc(ScopeData { parent: Some(parent), block: None, entries: vec![] }) |
93 | } | ||
94 | |||
95 | fn new_block_scope(&mut self, parent: ScopeId, block: BlockId) -> ScopeId { | ||
96 | self.scopes.alloc(ScopeData { parent: Some(parent), block: Some(block), entries: vec![] }) | ||
87 | } | 97 | } |
88 | 98 | ||
89 | fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { | 99 | fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { |
@@ -136,7 +146,11 @@ fn compute_block_scopes( | |||
136 | fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { | 146 | fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { |
137 | scopes.set_scope(expr, scope); | 147 | scopes.set_scope(expr, scope); |
138 | match &body[expr] { | 148 | match &body[expr] { |
139 | Expr::Block { statements, tail, .. } => { | 149 | Expr::Block { statements, tail, id, .. } => { |
150 | let scope = scopes.new_block_scope(scope, *id); | ||
151 | // Overwrite the old scope for the block expr, so that every block scope can be found | ||
152 | // via the block itself (important for blocks that only contain items, no expressions). | ||
153 | scopes.set_scope(expr, scope); | ||
140 | compute_block_scopes(&statements, *tail, body, scopes, scope); | 154 | compute_block_scopes(&statements, *tail, body, scopes, scope); |
141 | } | 155 | } |
142 | Expr::For { iterable, pat, body: body_expr, .. } => { | 156 | Expr::For { iterable, pat, body: body_expr, .. } => { |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index a92134ba7..bb43569d7 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | mod block; | 1 | mod block; |
2 | 2 | ||
3 | use base_db::{fixture::WithFixture, FilePosition, SourceDatabase}; | 3 | use base_db::{fixture::WithFixture, SourceDatabase}; |
4 | use expect_test::Expect; | 4 | use expect_test::Expect; |
5 | use test_utils::mark; | 5 | use test_utils::mark; |
6 | 6 | ||
7 | use crate::{test_db::TestDB, BlockId, ModuleDefId}; | 7 | use crate::{test_db::TestDB, ModuleDefId}; |
8 | 8 | ||
9 | use super::*; | 9 | use super::*; |
10 | 10 | ||
@@ -37,104 +37,8 @@ fn check_diagnostics(ra_fixture: &str) { | |||
37 | fn block_def_map_at(ra_fixture: &str) -> String { | 37 | fn block_def_map_at(ra_fixture: &str) -> String { |
38 | let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); | 38 | let (db, position) = crate::test_db::TestDB::with_position(ra_fixture); |
39 | 39 | ||
40 | let krate = db.crate_graph().iter().next().unwrap(); | 40 | let module = db.module_at_position(position); |
41 | let def_map = db.crate_def_map(krate); | 41 | module.def_map(&db).dump(&db) |
42 | |||
43 | let mut block = | ||
44 | block_at_pos(&db, &def_map, position).expect("couldn't find enclosing function or block"); | ||
45 | loop { | ||
46 | let def_map = db.block_def_map(block).unwrap_or_else(|| def_map.clone()); | ||
47 | let new_block = block_at_pos(&db, &def_map, position); | ||
48 | match new_block { | ||
49 | Some(new_block) => { | ||
50 | assert_ne!(block, new_block); | ||
51 | block = new_block; | ||
52 | } | ||
53 | None => { | ||
54 | return def_map.dump(&db); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | fn block_at_pos(db: &dyn DefDatabase, def_map: &DefMap, position: FilePosition) -> Option<BlockId> { | ||
61 | // Find the smallest (innermost) function containing the cursor. | ||
62 | let mut size = None; | ||
63 | let mut fn_def = None; | ||
64 | for (_, module) in def_map.modules() { | ||
65 | let file_id = module.definition_source(db).file_id; | ||
66 | if file_id != position.file_id.into() { | ||
67 | continue; | ||
68 | } | ||
69 | let root = db.parse_or_expand(file_id).unwrap(); | ||
70 | let ast_map = db.ast_id_map(file_id); | ||
71 | let item_tree = db.item_tree(file_id); | ||
72 | for decl in module.scope.declarations() { | ||
73 | if let ModuleDefId::FunctionId(it) = decl { | ||
74 | let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root); | ||
75 | let range = ast.syntax().text_range(); | ||
76 | |||
77 | if !range.contains(position.offset) { | ||
78 | continue; | ||
79 | } | ||
80 | |||
81 | let new_size = match size { | ||
82 | None => range.len(), | ||
83 | Some(size) => { | ||
84 | if range.len() < size { | ||
85 | range.len() | ||
86 | } else { | ||
87 | size | ||
88 | } | ||
89 | } | ||
90 | }; | ||
91 | if size != Some(new_size) { | ||
92 | size = Some(new_size); | ||
93 | fn_def = Some(it); | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | let (body, source_map) = db.body_with_source_map(fn_def?.into()); | ||
100 | |||
101 | // Now find the smallest encompassing block expression in the function body. | ||
102 | let mut size = None; | ||
103 | let mut block_id = None; | ||
104 | for (expr_id, expr) in body.exprs.iter() { | ||
105 | if let Expr::Block { id, .. } = expr { | ||
106 | if let Ok(ast) = source_map.expr_syntax(expr_id) { | ||
107 | if ast.file_id != position.file_id.into() { | ||
108 | continue; | ||
109 | } | ||
110 | |||
111 | let root = db.parse_or_expand(ast.file_id).unwrap(); | ||
112 | let ast = ast.value.to_node(&root); | ||
113 | let range = ast.syntax().text_range(); | ||
114 | |||
115 | if !range.contains(position.offset) { | ||
116 | continue; | ||
117 | } | ||
118 | |||
119 | let new_size = match size { | ||
120 | None => range.len(), | ||
121 | Some(size) => { | ||
122 | if range.len() < size { | ||
123 | range.len() | ||
124 | } else { | ||
125 | size | ||
126 | } | ||
127 | } | ||
128 | }; | ||
129 | if size != Some(new_size) { | ||
130 | size = Some(new_size); | ||
131 | block_id = Some(*id); | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | Some(block_id.expect("can't find block containing cursor")) | ||
138 | } | 42 | } |
139 | 43 | ||
140 | fn check_at(ra_fixture: &str, expect: Expect) { | 44 | fn check_at(ra_fixture: &str, expect: Expect) { |
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs index b599c6269..a5ec0883f 100644 --- a/crates/hir_def/src/body/tests/block.rs +++ b/crates/hir_def/src/body/tests/block.rs | |||
@@ -232,3 +232,30 @@ fn f() { | |||
232 | "#]], | 232 | "#]], |
233 | ) | 233 | ) |
234 | } | 234 | } |
235 | |||
236 | #[test] | ||
237 | fn super_does_not_resolve_to_block_module() { | ||
238 | check_at( | ||
239 | r#" | ||
240 | fn main() { | ||
241 | struct Struct {} | ||
242 | mod module { | ||
243 | use super::Struct; | ||
244 | |||
245 | $0 | ||
246 | } | ||
247 | } | ||
248 | "#, | ||
249 | expect![[r#" | ||
250 | block scope | ||
251 | Struct: t | ||
252 | module: t | ||
253 | |||
254 | block scope::module | ||
255 | Struct: _ | ||
256 | |||
257 | crate | ||
258 | main: v | ||
259 | "#]], | ||
260 | ); | ||
261 | } | ||
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. |
20 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 18 | pub 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 | } |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index ad2e9bcac..34ff07f3c 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -316,7 +316,7 @@ impl DefMap { | |||
316 | /// | 316 | /// |
317 | /// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns | 317 | /// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns |
318 | /// `None`, iteration continues. | 318 | /// `None`, iteration continues. |
319 | fn with_ancestor_maps<T>( | 319 | pub fn with_ancestor_maps<T>( |
320 | &self, | 320 | &self, |
321 | db: &dyn DefDatabase, | 321 | db: &dyn DefDatabase, |
322 | local_mod: LocalModuleId, | 322 | local_mod: LocalModuleId, |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index f9ad50301..a8467c88e 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -10,7 +10,6 @@ use rustc_hash::FxHashSet; | |||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | body::scope::{ExprScopes, ScopeId}, | 12 | body::scope::{ExprScopes, ScopeId}, |
13 | body::Body, | ||
14 | builtin_type::BuiltinType, | 13 | builtin_type::BuiltinType, |
15 | db::DefDatabase, | 14 | db::DefDatabase, |
16 | expr::{ExprId, PatId}, | 15 | expr::{ExprId, PatId}, |
@@ -58,8 +57,6 @@ enum Scope { | |||
58 | AdtScope(AdtId), | 57 | AdtScope(AdtId), |
59 | /// Local bindings | 58 | /// Local bindings |
60 | ExprScope(ExprScope), | 59 | ExprScope(ExprScope), |
61 | /// Temporary hack to support local items. | ||
62 | LocalItemsScope(Arc<Body>), | ||
63 | } | 60 | } |
64 | 61 | ||
65 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 62 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
@@ -169,13 +166,7 @@ impl Resolver { | |||
169 | for scope in self.scopes.iter().rev() { | 166 | for scope in self.scopes.iter().rev() { |
170 | match scope { | 167 | match scope { |
171 | Scope::ExprScope(_) => continue, | 168 | Scope::ExprScope(_) => continue, |
172 | Scope::GenericParams { .. } | 169 | Scope::GenericParams { .. } | Scope::ImplDefScope(_) if skip_to_mod => continue, |
173 | | Scope::ImplDefScope(_) | ||
174 | | Scope::LocalItemsScope(_) | ||
175 | if skip_to_mod => | ||
176 | { | ||
177 | continue | ||
178 | } | ||
179 | 170 | ||
180 | Scope::GenericParams { params, def } => { | 171 | Scope::GenericParams { params, def } => { |
181 | if let Some(local_id) = params.find_type_by_name(first_name) { | 172 | if let Some(local_id) = params.find_type_by_name(first_name) { |
@@ -199,41 +190,13 @@ impl Resolver { | |||
199 | } | 190 | } |
200 | } | 191 | } |
201 | Scope::ModuleScope(m) => { | 192 | Scope::ModuleScope(m) => { |
202 | let (module_def, idx) = m.crate_def_map.resolve_path( | 193 | if let Some(res) = m.resolve_path_in_type_ns(db, path) { |
203 | db, | 194 | return Some(res); |
204 | m.module_id, | ||
205 | &path, | ||
206 | BuiltinShadowMode::Other, | ||
207 | ); | ||
208 | let res = to_type_ns(module_def)?; | ||
209 | return Some((res, idx)); | ||
210 | } | ||
211 | Scope::LocalItemsScope(body) => { | ||
212 | let def = body.item_scope.get(first_name); | ||
213 | if let Some(res) = to_type_ns(def) { | ||
214 | return Some((res, None)); | ||
215 | } | 195 | } |
216 | } | 196 | } |
217 | } | 197 | } |
218 | } | 198 | } |
219 | return None; | 199 | None |
220 | fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> { | ||
221 | let res = match per_ns.take_types()? { | ||
222 | ModuleDefId::AdtId(it) => TypeNs::AdtId(it), | ||
223 | ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it), | ||
224 | |||
225 | ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), | ||
226 | ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), | ||
227 | |||
228 | ModuleDefId::TraitId(it) => TypeNs::TraitId(it), | ||
229 | |||
230 | ModuleDefId::FunctionId(_) | ||
231 | | ModuleDefId::ConstId(_) | ||
232 | | ModuleDefId::StaticId(_) | ||
233 | | ModuleDefId::ModuleId(_) => return None, | ||
234 | }; | ||
235 | Some(res) | ||
236 | } | ||
237 | } | 200 | } |
238 | 201 | ||
239 | pub fn resolve_path_in_type_ns_fully( | 202 | pub fn resolve_path_in_type_ns_fully( |
@@ -280,7 +243,6 @@ impl Resolver { | |||
280 | | Scope::ExprScope(_) | 243 | | Scope::ExprScope(_) |
281 | | Scope::GenericParams { .. } | 244 | | Scope::GenericParams { .. } |
282 | | Scope::ImplDefScope(_) | 245 | | Scope::ImplDefScope(_) |
283 | | Scope::LocalItemsScope(_) | ||
284 | if skip_to_mod => | 246 | if skip_to_mod => |
285 | { | 247 | { |
286 | continue | 248 | continue |
@@ -335,63 +297,14 @@ impl Resolver { | |||
335 | } | 297 | } |
336 | 298 | ||
337 | Scope::ModuleScope(m) => { | 299 | Scope::ModuleScope(m) => { |
338 | let (module_def, idx) = m.crate_def_map.resolve_path( | 300 | if let Some(def) = m.resolve_path_in_value_ns(db, path) { |
339 | db, | 301 | return Some(def); |
340 | m.module_id, | ||
341 | &path, | ||
342 | BuiltinShadowMode::Other, | ||
343 | ); | ||
344 | return match idx { | ||
345 | None => { | ||
346 | let value = to_value_ns(module_def)?; | ||
347 | Some(ResolveValueResult::ValueNs(value)) | ||
348 | } | ||
349 | Some(idx) => { | ||
350 | let ty = match module_def.take_types()? { | ||
351 | ModuleDefId::AdtId(it) => TypeNs::AdtId(it), | ||
352 | ModuleDefId::TraitId(it) => TypeNs::TraitId(it), | ||
353 | ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), | ||
354 | ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), | ||
355 | |||
356 | ModuleDefId::ModuleId(_) | ||
357 | | ModuleDefId::FunctionId(_) | ||
358 | | ModuleDefId::EnumVariantId(_) | ||
359 | | ModuleDefId::ConstId(_) | ||
360 | | ModuleDefId::StaticId(_) => return None, | ||
361 | }; | ||
362 | Some(ResolveValueResult::Partial(ty, idx)) | ||
363 | } | ||
364 | }; | ||
365 | } | ||
366 | Scope::LocalItemsScope(body) => { | ||
367 | // we don't bother looking in the builtin scope here because there are no builtin values | ||
368 | let def = to_value_ns(body.item_scope.get(first_name)); | ||
369 | |||
370 | if let Some(res) = def { | ||
371 | return Some(ResolveValueResult::ValueNs(res)); | ||
372 | } | 302 | } |
373 | } | 303 | } |
374 | } | 304 | } |
375 | } | 305 | } |
376 | return None; | 306 | |
377 | 307 | None | |
378 | fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> { | ||
379 | let res = match per_ns.take_values()? { | ||
380 | ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), | ||
381 | ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it), | ||
382 | ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it), | ||
383 | ModuleDefId::ConstId(it) => ValueNs::ConstId(it), | ||
384 | ModuleDefId::StaticId(it) => ValueNs::StaticId(it), | ||
385 | |||
386 | ModuleDefId::AdtId(AdtId::EnumId(_)) | ||
387 | | ModuleDefId::AdtId(AdtId::UnionId(_)) | ||
388 | | ModuleDefId::TraitId(_) | ||
389 | | ModuleDefId::TypeAliasId(_) | ||
390 | | ModuleDefId::BuiltinType(_) | ||
391 | | ModuleDefId::ModuleId(_) => return None, | ||
392 | }; | ||
393 | Some(res) | ||
394 | } | ||
395 | } | 308 | } |
396 | 309 | ||
397 | pub fn resolve_path_in_value_ns_fully( | 310 | pub fn resolve_path_in_value_ns_fully( |
@@ -410,11 +323,6 @@ impl Resolver { | |||
410 | db: &dyn DefDatabase, | 323 | db: &dyn DefDatabase, |
411 | path: &ModPath, | 324 | path: &ModPath, |
412 | ) -> Option<MacroDefId> { | 325 | ) -> Option<MacroDefId> { |
413 | // Search item scope legacy macro first | ||
414 | if let Some(def) = self.resolve_local_macro_def(path) { | ||
415 | return Some(def); | ||
416 | } | ||
417 | |||
418 | let (item_map, module) = self.module_scope()?; | 326 | let (item_map, module) = self.module_scope()?; |
419 | item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() | 327 | item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros() |
420 | } | 328 | } |
@@ -447,16 +355,6 @@ impl Resolver { | |||
447 | }) | 355 | }) |
448 | } | 356 | } |
449 | 357 | ||
450 | fn resolve_local_macro_def(&self, path: &ModPath) -> Option<MacroDefId> { | ||
451 | let name = path.as_ident()?; | ||
452 | self.scopes.iter().rev().find_map(|scope| { | ||
453 | if let Scope::LocalItemsScope(body) = scope { | ||
454 | return body.item_scope.get_legacy_macro(name); | ||
455 | } | ||
456 | None | ||
457 | }) | ||
458 | } | ||
459 | |||
460 | pub fn module(&self) -> Option<ModuleId> { | 358 | pub fn module(&self) -> Option<ModuleId> { |
461 | let (def_map, local_id) = self.module_scope()?; | 359 | let (def_map, local_id) = self.module_scope()?; |
462 | Some(def_map.module_id(local_id)) | 360 | Some(def_map.module_id(local_id)) |
@@ -538,9 +436,6 @@ impl Scope { | |||
538 | }); | 436 | }); |
539 | } | 437 | } |
540 | } | 438 | } |
541 | Scope::LocalItemsScope(body) => body.item_scope.entries().for_each(|(name, def)| { | ||
542 | f(name.clone(), ScopeDef::PerNs(def)); | ||
543 | }), | ||
544 | &Scope::GenericParams { ref params, def: parent } => { | 439 | &Scope::GenericParams { ref params, def: parent } => { |
545 | for (local_id, param) in params.types.iter() { | 440 | for (local_id, param) in params.types.iter() { |
546 | if let Some(ref name) = param.name { | 441 | if let Some(ref name) = param.name { |
@@ -584,10 +479,19 @@ pub fn resolver_for_scope( | |||
584 | scope_id: Option<ScopeId>, | 479 | scope_id: Option<ScopeId>, |
585 | ) -> Resolver { | 480 | ) -> Resolver { |
586 | let mut r = owner.resolver(db); | 481 | let mut r = owner.resolver(db); |
587 | r = r.push_local_items_scope(db.body(owner)); | ||
588 | let scopes = db.expr_scopes(owner); | 482 | let scopes = db.expr_scopes(owner); |
589 | let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>(); | 483 | let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>(); |
590 | for scope in scope_chain.into_iter().rev() { | 484 | for scope in scope_chain.into_iter().rev() { |
485 | if let Some(block) = scopes.block(scope) { | ||
486 | if let Some(def_map) = db.block_def_map(block) { | ||
487 | let root = def_map.root(); | ||
488 | r = r.push_module_scope(def_map, root); | ||
489 | // FIXME: This adds as many module scopes as there are blocks, but resolving in each | ||
490 | // already traverses all parents, so this is O(n²). I think we could only store the | ||
491 | // innermost module scope instead? | ||
492 | } | ||
493 | } | ||
494 | |||
591 | r = r.push_expr_scope(owner, Arc::clone(&scopes), scope); | 495 | r = r.push_expr_scope(owner, Arc::clone(&scopes), scope); |
592 | } | 496 | } |
593 | r | 497 | r |
@@ -612,10 +516,6 @@ impl Resolver { | |||
612 | self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) | 516 | self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) |
613 | } | 517 | } |
614 | 518 | ||
615 | fn push_local_items_scope(self, body: Arc<Body>) -> Resolver { | ||
616 | self.push_scope(Scope::LocalItemsScope(body)) | ||
617 | } | ||
618 | |||
619 | fn push_expr_scope( | 519 | fn push_expr_scope( |
620 | self, | 520 | self, |
621 | owner: DefWithBodyId, | 521 | owner: DefWithBodyId, |
@@ -626,6 +526,85 @@ impl Resolver { | |||
626 | } | 526 | } |
627 | } | 527 | } |
628 | 528 | ||
529 | impl ModuleItemMap { | ||
530 | fn resolve_path_in_value_ns( | ||
531 | &self, | ||
532 | db: &dyn DefDatabase, | ||
533 | path: &ModPath, | ||
534 | ) -> Option<ResolveValueResult> { | ||
535 | let (module_def, idx) = | ||
536 | self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); | ||
537 | match idx { | ||
538 | None => { | ||
539 | let value = to_value_ns(module_def)?; | ||
540 | Some(ResolveValueResult::ValueNs(value)) | ||
541 | } | ||
542 | Some(idx) => { | ||
543 | let ty = match module_def.take_types()? { | ||
544 | ModuleDefId::AdtId(it) => TypeNs::AdtId(it), | ||
545 | ModuleDefId::TraitId(it) => TypeNs::TraitId(it), | ||
546 | ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), | ||
547 | ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), | ||
548 | |||
549 | ModuleDefId::ModuleId(_) | ||
550 | | ModuleDefId::FunctionId(_) | ||
551 | | ModuleDefId::EnumVariantId(_) | ||
552 | | ModuleDefId::ConstId(_) | ||
553 | | ModuleDefId::StaticId(_) => return None, | ||
554 | }; | ||
555 | Some(ResolveValueResult::Partial(ty, idx)) | ||
556 | } | ||
557 | } | ||
558 | } | ||
559 | |||
560 | fn resolve_path_in_type_ns( | ||
561 | &self, | ||
562 | db: &dyn DefDatabase, | ||
563 | path: &ModPath, | ||
564 | ) -> Option<(TypeNs, Option<usize>)> { | ||
565 | let (module_def, idx) = | ||
566 | self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); | ||
567 | let res = to_type_ns(module_def)?; | ||
568 | Some((res, idx)) | ||
569 | } | ||
570 | } | ||
571 | |||
572 | fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> { | ||
573 | let res = match per_ns.take_values()? { | ||
574 | ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), | ||
575 | ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it), | ||
576 | ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it), | ||
577 | ModuleDefId::ConstId(it) => ValueNs::ConstId(it), | ||
578 | ModuleDefId::StaticId(it) => ValueNs::StaticId(it), | ||
579 | |||
580 | ModuleDefId::AdtId(AdtId::EnumId(_)) | ||
581 | | ModuleDefId::AdtId(AdtId::UnionId(_)) | ||
582 | | ModuleDefId::TraitId(_) | ||
583 | | ModuleDefId::TypeAliasId(_) | ||
584 | | ModuleDefId::BuiltinType(_) | ||
585 | | ModuleDefId::ModuleId(_) => return None, | ||
586 | }; | ||
587 | Some(res) | ||
588 | } | ||
589 | |||
590 | fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> { | ||
591 | let res = match per_ns.take_types()? { | ||
592 | ModuleDefId::AdtId(it) => TypeNs::AdtId(it), | ||
593 | ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it), | ||
594 | |||
595 | ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), | ||
596 | ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), | ||
597 | |||
598 | ModuleDefId::TraitId(it) => TypeNs::TraitId(it), | ||
599 | |||
600 | ModuleDefId::FunctionId(_) | ||
601 | | ModuleDefId::ConstId(_) | ||
602 | | ModuleDefId::StaticId(_) | ||
603 | | ModuleDefId::ModuleId(_) => return None, | ||
604 | }; | ||
605 | Some(res) | ||
606 | } | ||
607 | |||
629 | pub trait HasResolver: Copy { | 608 | pub trait HasResolver: Copy { |
630 | /// Builds a resolver for type references inside this def. | 609 | /// Builds a resolver for type references inside this def. |
631 | fn resolver(self, db: &dyn DefDatabase) -> Resolver; | 610 | fn resolver(self, db: &dyn DefDatabase) -> Resolver; |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 6665d902d..eda982c85 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -5,17 +5,17 @@ use std::{ | |||
5 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; | 8 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, Upcast}; |
9 | use base_db::{AnchoredPath, SourceDatabase}; | 9 | use base_db::{AnchoredPath, SourceDatabase}; |
10 | use hir_expand::db::AstDatabase; | ||
11 | use hir_expand::diagnostics::Diagnostic; | 10 | use hir_expand::diagnostics::Diagnostic; |
12 | use hir_expand::diagnostics::DiagnosticSinkBuilder; | 11 | use hir_expand::diagnostics::DiagnosticSinkBuilder; |
12 | use hir_expand::{db::AstDatabase, InFile}; | ||
13 | use rustc_hash::FxHashMap; | 13 | use rustc_hash::FxHashMap; |
14 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | use syntax::{TextRange, TextSize}; | 15 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; |
16 | use test_utils::extract_annotations; | 16 | use test_utils::extract_annotations; |
17 | 17 | ||
18 | use crate::{db::DefDatabase, ModuleDefId, ModuleId}; | 18 | use crate::{db::DefDatabase, nameres::DefMap, Lookup, ModuleDefId, ModuleId}; |
19 | 19 | ||
20 | #[salsa::database( | 20 | #[salsa::database( |
21 | base_db::SourceDatabaseExtStorage, | 21 | base_db::SourceDatabaseExtStorage, |
@@ -84,6 +84,97 @@ impl TestDB { | |||
84 | panic!("Can't find module for file") | 84 | panic!("Can't find module for file") |
85 | } | 85 | } |
86 | 86 | ||
87 | pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { | ||
88 | let file_module = self.module_for_file(position.file_id); | ||
89 | let mut def_map = file_module.def_map(self); | ||
90 | |||
91 | def_map = match self.block_at_position(&def_map, position) { | ||
92 | Some(it) => it, | ||
93 | None => return file_module, | ||
94 | }; | ||
95 | loop { | ||
96 | let new_map = self.block_at_position(&def_map, position); | ||
97 | match new_map { | ||
98 | Some(new_block) if !Arc::ptr_eq(&new_block, &def_map) => { | ||
99 | def_map = new_block; | ||
100 | } | ||
101 | _ => { | ||
102 | // FIXME: handle `mod` inside block expression | ||
103 | return def_map.module_id(def_map.root()); | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> { | ||
110 | // Find the smallest (innermost) function in `def_map` containing the cursor. | ||
111 | let mut size = None; | ||
112 | let mut fn_def = None; | ||
113 | for (_, module) in def_map.modules() { | ||
114 | let file_id = module.definition_source(self).file_id; | ||
115 | if file_id != position.file_id.into() { | ||
116 | continue; | ||
117 | } | ||
118 | let root = self.parse_or_expand(file_id).unwrap(); | ||
119 | let ast_map = self.ast_id_map(file_id); | ||
120 | let item_tree = self.item_tree(file_id); | ||
121 | for decl in module.scope.declarations() { | ||
122 | if let ModuleDefId::FunctionId(it) = decl { | ||
123 | let ast = | ||
124 | ast_map.get(item_tree[it.lookup(self).id.value].ast_id).to_node(&root); | ||
125 | let range = ast.syntax().text_range(); | ||
126 | |||
127 | if !range.contains(position.offset) { | ||
128 | continue; | ||
129 | } | ||
130 | |||
131 | let new_size = match size { | ||
132 | None => range.len(), | ||
133 | Some(size) => { | ||
134 | if range.len() < size { | ||
135 | range.len() | ||
136 | } else { | ||
137 | size | ||
138 | } | ||
139 | } | ||
140 | }; | ||
141 | if size != Some(new_size) { | ||
142 | size = Some(new_size); | ||
143 | fn_def = Some(it); | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | // Find the innermost block expression that has a `DefMap`. | ||
150 | let def_with_body = fn_def?.into(); | ||
151 | let (_, source_map) = self.body_with_source_map(def_with_body); | ||
152 | let scopes = self.expr_scopes(def_with_body); | ||
153 | let root = self.parse(position.file_id); | ||
154 | |||
155 | let scope_iter = algo::ancestors_at_offset(&root.syntax_node(), position.offset) | ||
156 | .filter_map(|node| { | ||
157 | let block = ast::BlockExpr::cast(node)?; | ||
158 | let expr = ast::Expr::from(block); | ||
159 | let expr_id = source_map.node_expr(InFile::new(position.file_id.into(), &expr))?; | ||
160 | let scope = scopes.scope_for(expr_id).unwrap(); | ||
161 | Some(scope) | ||
162 | }); | ||
163 | |||
164 | for scope in scope_iter { | ||
165 | let containing_blocks = | ||
166 | scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope)); | ||
167 | |||
168 | for block in containing_blocks { | ||
169 | if let Some(def_map) = self.block_def_map(block) { | ||
170 | return Some(def_map); | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | |||
175 | None | ||
176 | } | ||
177 | |||
87 | pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> { | 178 | pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> { |
88 | *self.events.lock().unwrap() = Some(Vec::new()); | 179 | *self.events.lock().unwrap() = Some(Vec::new()); |
89 | f(); | 180 | f(); |