aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_def/src/body/scope.rs22
-rw-r--r--crates/hir_def/src/body/tests.rs104
-rw-r--r--crates/hir_def/src/body/tests/block.rs27
-rw-r--r--crates/hir_def/src/find_path.rs101
-rw-r--r--crates/hir_def/src/nameres.rs2
-rw-r--r--crates/hir_def/src/resolver.rs215
-rw-r--r--crates/hir_def/src/test_db.rs99
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
15pub type ScopeId = Idx<ScopeData>; 15pub type ScopeId = Idx<ScopeData>;
@@ -39,6 +39,7 @@ impl ScopeEntry {
39#[derive(Debug, PartialEq, Eq)] 39#[derive(Debug, PartialEq, Eq)]
40pub struct ScopeData { 40pub 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(
136fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { 146fn 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 @@
1mod block; 1mod block;
2 2
3use base_db::{fixture::WithFixture, FilePosition, SourceDatabase}; 3use base_db::{fixture::WithFixture, SourceDatabase};
4use expect_test::Expect; 4use expect_test::Expect;
5use test_utils::mark; 5use test_utils::mark;
6 6
7use crate::{test_db::TestDB, BlockId, ModuleDefId}; 7use crate::{test_db::TestDB, ModuleDefId};
8 8
9use super::*; 9use super::*;
10 10
@@ -37,104 +37,8 @@ fn check_diagnostics(ra_fixture: &str) {
37fn block_def_map_at(ra_fixture: &str) -> String { 37fn 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
60fn 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
140fn check_at(ra_fixture: &str, expect: Expect) { 44fn 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]
237fn super_does_not_resolve_to_block_module() {
238 check_at(
239 r#"
240fn 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.
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}
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
11use crate::{ 11use 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
529impl 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
572fn 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
590fn 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
629pub trait HasResolver: Copy { 608pub 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
8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; 8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, Upcast};
9use base_db::{AnchoredPath, SourceDatabase}; 9use base_db::{AnchoredPath, SourceDatabase};
10use hir_expand::db::AstDatabase;
11use hir_expand::diagnostics::Diagnostic; 10use hir_expand::diagnostics::Diagnostic;
12use hir_expand::diagnostics::DiagnosticSinkBuilder; 11use hir_expand::diagnostics::DiagnosticSinkBuilder;
12use hir_expand::{db::AstDatabase, InFile};
13use rustc_hash::FxHashMap; 13use rustc_hash::FxHashMap;
14use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15use syntax::{TextRange, TextSize}; 15use syntax::{algo, ast, AstNode, TextRange, TextSize};
16use test_utils::extract_annotations; 16use test_utils::extract_annotations;
17 17
18use crate::{db::DefDatabase, ModuleDefId, ModuleId}; 18use 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();