diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-01-21 15:28:40 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-01-21 15:28:40 +0000 |
commit | 47a70aadcedbcf28ad6d1ea59b77bf0e11493de0 (patch) | |
tree | e5f494422dacc1a6bcae2b696d06f937b64d9163 /crates/hir_def/src | |
parent | 323138f32ea74cfe9f5381e9e170cf87e7592818 (diff) | |
parent | ec4a1dc297eb90dde4c22c682a35606aaa50b4d4 (diff) |
Merge #7375
7375: Add support for running name resolution in block expressions r=jonas-schievink a=jonas-schievink
This adds a `block_def_map` query that runs the name resolution algorithm on a block expression, and returns a `DefMap` that stores links to the parent `DefMap` (either the containing block or the crate-level `DefMap`). Blocks with no inner items return the parent's `DefMap` as-is, to avoid creating unnecessarily long `DefMap` chains.
Path resolution is updated to recurse into the parent `DefMap` after looking up a path in the original `DefMap`.
I've added a few new tests for this, but outside of those this isn't used yet.
bors r+
Co-authored-by: Jonas Schievink <[email protected]>
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r-- | crates/hir_def/src/db.rs | 7 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree.rs | 7 | ||||
-rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 38 | ||||
-rw-r--r-- | crates/hir_def/src/nameres.rs | 95 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/collector.rs | 58 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/path_resolution.rs | 37 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests.rs | 20 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/block.rs | 72 |
8 files changed, 283 insertions, 51 deletions
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index 91c8d45cd..a87c80b8a 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -2,9 +2,9 @@ | |||
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; | 4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; |
5 | use hir_expand::{db::AstDatabase, HirFileId}; | 5 | use hir_expand::{db::AstDatabase, AstId, HirFileId}; |
6 | use la_arena::ArenaMap; | 6 | use la_arena::ArenaMap; |
7 | use syntax::SmolStr; | 7 | use syntax::{ast, SmolStr}; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | adt::{EnumData, StructData}, | 10 | adt::{EnumData, StructData}, |
@@ -55,6 +55,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
55 | #[salsa::invoke(DefMap::crate_def_map_query)] | 55 | #[salsa::invoke(DefMap::crate_def_map_query)] |
56 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; | 56 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; |
57 | 57 | ||
58 | #[salsa::invoke(DefMap::block_def_map_query)] | ||
59 | fn block_def_map(&self, krate: CrateId, block: AstId<ast::BlockExpr>) -> Arc<DefMap>; | ||
60 | |||
58 | #[salsa::invoke(StructData::struct_data_query)] | 61 | #[salsa::invoke(StructData::struct_data_query)] |
59 | fn struct_data(&self, id: StructId) -> Arc<StructData>; | 62 | fn struct_data(&self, id: StructId) -> Arc<StructData>; |
60 | #[salsa::invoke(StructData::union_data_query)] | 63 | #[salsa::invoke(StructData::union_data_query)] |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 6494cebd3..1226d7d85 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -195,6 +195,13 @@ impl ItemTree { | |||
195 | } | 195 | } |
196 | } | 196 | } |
197 | 197 | ||
198 | pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] { | ||
199 | match &self.data { | ||
200 | Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]), | ||
201 | None => &[], | ||
202 | } | ||
203 | } | ||
204 | |||
198 | pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source { | 205 | pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source { |
199 | // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty | 206 | // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty |
200 | // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with). | 207 | // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with). |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 56fe569ff..61cbbbc8f 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -151,23 +151,31 @@ impl Ctx { | |||
151 | fn collect_inner_items(&mut self, container: &SyntaxNode) { | 151 | fn collect_inner_items(&mut self, container: &SyntaxNode) { |
152 | let forced_vis = self.forced_visibility.take(); | 152 | let forced_vis = self.forced_visibility.take(); |
153 | 153 | ||
154 | let mut current_block = None; | 154 | let mut block_stack = Vec::new(); |
155 | for event in container.preorder().skip(1) { | 155 | for event in container.preorder().skip(1) { |
156 | if let WalkEvent::Enter(node) = event { | 156 | match event { |
157 | match_ast! { | 157 | WalkEvent::Enter(node) => { |
158 | match node { | 158 | match_ast! { |
159 | ast::BlockExpr(block) => { | 159 | match node { |
160 | current_block = Some(self.source_ast_id_map.ast_id(&block)); | 160 | ast::BlockExpr(block) => { |
161 | }, | 161 | block_stack.push(self.source_ast_id_map.ast_id(&block)); |
162 | ast::Item(item) => { | 162 | }, |
163 | let mod_items = self.lower_mod_item(&item, true); | 163 | ast::Item(item) => { |
164 | if let (Some(mod_items), Some(block)) = (mod_items, current_block) { | 164 | let mod_items = self.lower_mod_item(&item, true); |
165 | if !mod_items.0.is_empty() { | 165 | let current_block = block_stack.last(); |
166 | self.data().inner_items.entry(block).or_default().extend(mod_items.0.iter().copied()); | 166 | if let (Some(mod_items), Some(block)) = (mod_items, current_block) { |
167 | if !mod_items.0.is_empty() { | ||
168 | self.data().inner_items.entry(*block).or_default().extend(mod_items.0.iter().copied()); | ||
169 | } | ||
167 | } | 170 | } |
168 | } | 171 | }, |
169 | }, | 172 | _ => {} |
170 | _ => {} | 173 | } |
174 | } | ||
175 | } | ||
176 | WalkEvent::Leave(node) => { | ||
177 | if ast::BlockExpr::cast(node).is_some() { | ||
178 | block_stack.pop(); | ||
171 | } | 179 | } |
172 | } | 180 | } |
173 | } | 181 | } |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index a3200c710..93931a21a 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -61,7 +61,7 @@ use hir_expand::{diagnostics::DiagnosticSink, name::Name, InFile}; | |||
61 | use la_arena::Arena; | 61 | use la_arena::Arena; |
62 | use rustc_hash::FxHashMap; | 62 | use rustc_hash::FxHashMap; |
63 | use stdx::format_to; | 63 | use stdx::format_to; |
64 | use syntax::ast; | 64 | use syntax::{ast, AstNode}; |
65 | 65 | ||
66 | use crate::{ | 66 | use crate::{ |
67 | db::DefDatabase, | 67 | db::DefDatabase, |
@@ -75,6 +75,7 @@ use crate::{ | |||
75 | /// Contains all top-level defs from a macro-expanded crate | 75 | /// Contains all top-level defs from a macro-expanded crate |
76 | #[derive(Debug, PartialEq, Eq)] | 76 | #[derive(Debug, PartialEq, Eq)] |
77 | pub struct DefMap { | 77 | pub struct DefMap { |
78 | parent: Option<Arc<DefMap>>, | ||
78 | root: LocalModuleId, | 79 | root: LocalModuleId, |
79 | modules: Arena<ModuleData>, | 80 | modules: Arena<ModuleData>, |
80 | krate: CrateId, | 81 | krate: CrateId, |
@@ -181,24 +182,50 @@ impl DefMap { | |||
181 | let _p = profile::span("crate_def_map_query").detail(|| { | 182 | let _p = profile::span("crate_def_map_query").detail(|| { |
182 | db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() | 183 | db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() |
183 | }); | 184 | }); |
184 | let def_map = { | 185 | let edition = db.crate_graph()[krate].edition; |
185 | let edition = db.crate_graph()[krate].edition; | 186 | let def_map = DefMap::empty(krate, edition); |
186 | let mut modules: Arena<ModuleData> = Arena::default(); | 187 | let def_map = collector::collect_defs(db, def_map, None); |
187 | let root = modules.alloc(ModuleData::default()); | 188 | Arc::new(def_map) |
188 | DefMap { | 189 | } |
189 | krate, | 190 | |
190 | edition, | 191 | pub(crate) fn block_def_map_query( |
191 | extern_prelude: FxHashMap::default(), | 192 | db: &dyn DefDatabase, |
192 | prelude: None, | 193 | krate: CrateId, |
193 | root, | 194 | block: AstId<ast::BlockExpr>, |
194 | modules, | 195 | ) -> Arc<DefMap> { |
195 | diagnostics: Vec::new(), | 196 | let item_tree = db.item_tree(block.file_id); |
196 | } | 197 | let block_items = item_tree.inner_items_of_block(block.value); |
197 | }; | 198 | |
198 | let def_map = collector::collect_defs(db, def_map); | 199 | let parent = parent_def_map(db, krate, block); |
200 | |||
201 | if block_items.is_empty() { | ||
202 | // If there are no inner items, nothing new is brought into scope, so we can just return | ||
203 | // the parent DefMap. This keeps DefMap parent chains short. | ||
204 | return parent; | ||
205 | } | ||
206 | |||
207 | let mut def_map = DefMap::empty(krate, parent.edition); | ||
208 | def_map.parent = Some(parent); | ||
209 | |||
210 | let def_map = collector::collect_defs(db, def_map, Some(block.value)); | ||
199 | Arc::new(def_map) | 211 | Arc::new(def_map) |
200 | } | 212 | } |
201 | 213 | ||
214 | fn empty(krate: CrateId, edition: Edition) -> DefMap { | ||
215 | let mut modules: Arena<ModuleData> = Arena::default(); | ||
216 | let root = modules.alloc(ModuleData::default()); | ||
217 | DefMap { | ||
218 | parent: None, | ||
219 | krate, | ||
220 | edition, | ||
221 | extern_prelude: FxHashMap::default(), | ||
222 | prelude: None, | ||
223 | root, | ||
224 | modules, | ||
225 | diagnostics: Vec::new(), | ||
226 | } | ||
227 | } | ||
228 | |||
202 | pub fn add_diagnostics( | 229 | pub fn add_diagnostics( |
203 | &self, | 230 | &self, |
204 | db: &dyn DefDatabase, | 231 | db: &dyn DefDatabase, |
@@ -251,7 +278,12 @@ impl DefMap { | |||
251 | // even), as this should be a great debugging aid. | 278 | // even), as this should be a great debugging aid. |
252 | pub fn dump(&self) -> String { | 279 | pub fn dump(&self) -> String { |
253 | let mut buf = String::new(); | 280 | let mut buf = String::new(); |
254 | go(&mut buf, self, "crate", self.root); | 281 | let mut current_map = self; |
282 | while let Some(parent) = ¤t_map.parent { | ||
283 | go(&mut buf, current_map, "block scope", current_map.root); | ||
284 | current_map = &**parent; | ||
285 | } | ||
286 | go(&mut buf, current_map, "crate", current_map.root); | ||
255 | return buf; | 287 | return buf; |
256 | 288 | ||
257 | fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) { | 289 | fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) { |
@@ -303,6 +335,35 @@ impl ModuleData { | |||
303 | } | 335 | } |
304 | } | 336 | } |
305 | 337 | ||
338 | fn parent_def_map( | ||
339 | db: &dyn DefDatabase, | ||
340 | krate: CrateId, | ||
341 | block: AstId<ast::BlockExpr>, | ||
342 | ) -> Arc<DefMap> { | ||
343 | // FIXME: store this info in the item tree instead of reparsing here | ||
344 | let ast_id_map = db.ast_id_map(block.file_id); | ||
345 | let block_ptr = ast_id_map.get(block.value); | ||
346 | let root = match db.parse_or_expand(block.file_id) { | ||
347 | Some(it) => it, | ||
348 | None => { | ||
349 | return Arc::new(DefMap::empty(krate, Edition::Edition2018)); | ||
350 | } | ||
351 | }; | ||
352 | let ast = block_ptr.to_node(&root); | ||
353 | |||
354 | for ancestor in ast.syntax().ancestors().skip(1) { | ||
355 | if let Some(block_expr) = ast::BlockExpr::cast(ancestor) { | ||
356 | let ancestor_id = ast_id_map.ast_id(&block_expr); | ||
357 | let ast_id = InFile::new(block.file_id, ancestor_id); | ||
358 | let parent_map = db.block_def_map(krate, ast_id); | ||
359 | return parent_map; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | // No enclosing block scope, so the parent is the crate-level DefMap. | ||
364 | db.crate_def_map(krate) | ||
365 | } | ||
366 | |||
306 | #[derive(Debug, Clone, PartialEq, Eq)] | 367 | #[derive(Debug, Clone, PartialEq, Eq)] |
307 | pub enum ModuleSource { | 368 | pub enum ModuleSource { |
308 | SourceFile(ast::SourceFile), | 369 | SourceFile(ast::SourceFile), |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 61da56340..cd68efbe6 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -45,7 +45,11 @@ const GLOB_RECURSION_LIMIT: usize = 100; | |||
45 | const EXPANSION_DEPTH_LIMIT: usize = 128; | 45 | const EXPANSION_DEPTH_LIMIT: usize = 128; |
46 | const FIXED_POINT_LIMIT: usize = 8192; | 46 | const FIXED_POINT_LIMIT: usize = 8192; |
47 | 47 | ||
48 | pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap { | 48 | pub(super) fn collect_defs( |
49 | db: &dyn DefDatabase, | ||
50 | mut def_map: DefMap, | ||
51 | block: Option<FileAstId<ast::BlockExpr>>, | ||
52 | ) -> DefMap { | ||
49 | let crate_graph = db.crate_graph(); | 53 | let crate_graph = db.crate_graph(); |
50 | 54 | ||
51 | // populate external prelude | 55 | // populate external prelude |
@@ -93,6 +97,14 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap | |||
93 | exports_proc_macros: false, | 97 | exports_proc_macros: false, |
94 | from_glob_import: Default::default(), | 98 | from_glob_import: Default::default(), |
95 | }; | 99 | }; |
100 | match block { | ||
101 | Some(block) => { | ||
102 | collector.seed_with_inner(block); | ||
103 | } | ||
104 | None => { | ||
105 | collector.seed_with_top_level(); | ||
106 | } | ||
107 | } | ||
96 | collector.collect(); | 108 | collector.collect(); |
97 | collector.finish() | 109 | collector.finish() |
98 | } | 110 | } |
@@ -228,7 +240,7 @@ struct DefCollector<'a> { | |||
228 | } | 240 | } |
229 | 241 | ||
230 | impl DefCollector<'_> { | 242 | impl DefCollector<'_> { |
231 | fn collect(&mut self) { | 243 | fn seed_with_top_level(&mut self) { |
232 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; | 244 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; |
233 | let item_tree = self.db.item_tree(file_id.into()); | 245 | let item_tree = self.db.item_tree(file_id.into()); |
234 | let module_id = self.def_map.root; | 246 | let module_id = self.def_map.root; |
@@ -248,7 +260,31 @@ impl DefCollector<'_> { | |||
248 | } | 260 | } |
249 | .collect(item_tree.top_level_items()); | 261 | .collect(item_tree.top_level_items()); |
250 | } | 262 | } |
263 | } | ||
264 | |||
265 | fn seed_with_inner(&mut self, block: FileAstId<ast::BlockExpr>) { | ||
266 | let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; | ||
267 | let item_tree = self.db.item_tree(file_id.into()); | ||
268 | let module_id = self.def_map.root; | ||
269 | self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; | ||
270 | if item_tree | ||
271 | .top_level_attrs(self.db, self.def_map.krate) | ||
272 | .cfg() | ||
273 | .map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) | ||
274 | { | ||
275 | ModCollector { | ||
276 | def_collector: &mut *self, | ||
277 | macro_depth: 0, | ||
278 | module_id, | ||
279 | file_id: file_id.into(), | ||
280 | item_tree: &item_tree, | ||
281 | mod_dir: ModDir::root(), | ||
282 | } | ||
283 | .collect(item_tree.inner_items_of_block(block)); | ||
284 | } | ||
285 | } | ||
251 | 286 | ||
287 | fn collect(&mut self) { | ||
252 | // main name resolution fixed-point loop. | 288 | // main name resolution fixed-point loop. |
253 | let mut i = 0; | 289 | let mut i = 0; |
254 | loop { | 290 | loop { |
@@ -1470,7 +1506,6 @@ impl ModCollector<'_, '_> { | |||
1470 | mod tests { | 1506 | mod tests { |
1471 | use crate::{db::DefDatabase, test_db::TestDB}; | 1507 | use crate::{db::DefDatabase, test_db::TestDB}; |
1472 | use base_db::{fixture::WithFixture, SourceDatabase}; | 1508 | use base_db::{fixture::WithFixture, SourceDatabase}; |
1473 | use la_arena::Arena; | ||
1474 | 1509 | ||
1475 | use super::*; | 1510 | use super::*; |
1476 | 1511 | ||
@@ -1489,6 +1524,7 @@ mod tests { | |||
1489 | exports_proc_macros: false, | 1524 | exports_proc_macros: false, |
1490 | from_glob_import: Default::default(), | 1525 | from_glob_import: Default::default(), |
1491 | }; | 1526 | }; |
1527 | collector.seed_with_top_level(); | ||
1492 | collector.collect(); | 1528 | collector.collect(); |
1493 | collector.def_map | 1529 | collector.def_map |
1494 | } | 1530 | } |
@@ -1497,20 +1533,8 @@ mod tests { | |||
1497 | let (db, _file_id) = TestDB::with_single_file(&code); | 1533 | let (db, _file_id) = TestDB::with_single_file(&code); |
1498 | let krate = db.test_crate(); | 1534 | let krate = db.test_crate(); |
1499 | 1535 | ||
1500 | let def_map = { | 1536 | let edition = db.crate_graph()[krate].edition; |
1501 | let edition = db.crate_graph()[krate].edition; | 1537 | let def_map = DefMap::empty(krate, edition); |
1502 | let mut modules: Arena<ModuleData> = Arena::default(); | ||
1503 | let root = modules.alloc(ModuleData::default()); | ||
1504 | DefMap { | ||
1505 | krate, | ||
1506 | edition, | ||
1507 | extern_prelude: FxHashMap::default(), | ||
1508 | prelude: None, | ||
1509 | root, | ||
1510 | modules, | ||
1511 | diagnostics: Vec::new(), | ||
1512 | } | ||
1513 | }; | ||
1514 | do_collect_defs(&db, def_map) | 1538 | do_collect_defs(&db, def_map) |
1515 | } | 1539 | } |
1516 | 1540 | ||
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index 096a7d0ac..ec90f4e65 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -104,6 +104,43 @@ impl DefMap { | |||
104 | path: &ModPath, | 104 | path: &ModPath, |
105 | shadow: BuiltinShadowMode, | 105 | shadow: BuiltinShadowMode, |
106 | ) -> ResolvePathResult { | 106 | ) -> ResolvePathResult { |
107 | let mut result = ResolvePathResult::empty(ReachedFixedPoint::No); | ||
108 | result.segment_index = Some(usize::max_value()); | ||
109 | |||
110 | let mut current_map = self; | ||
111 | loop { | ||
112 | let new = current_map.resolve_path_fp_with_macro_single( | ||
113 | db, | ||
114 | mode, | ||
115 | original_module, | ||
116 | path, | ||
117 | shadow, | ||
118 | ); | ||
119 | |||
120 | // Merge `new` into `result`. | ||
121 | result.resolved_def = result.resolved_def.or(new.resolved_def); | ||
122 | if result.reached_fixedpoint == ReachedFixedPoint::No { | ||
123 | result.reached_fixedpoint = new.reached_fixedpoint; | ||
124 | } | ||
125 | // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates? | ||
126 | result.krate = result.krate.or(new.krate); | ||
127 | result.segment_index = result.segment_index.min(new.segment_index); | ||
128 | |||
129 | match ¤t_map.parent { | ||
130 | Some(map) => current_map = map, | ||
131 | None => return result, | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | |||
136 | pub(super) fn resolve_path_fp_with_macro_single( | ||
137 | &self, | ||
138 | db: &dyn DefDatabase, | ||
139 | mode: ResolveMode, | ||
140 | original_module: LocalModuleId, | ||
141 | path: &ModPath, | ||
142 | shadow: BuiltinShadowMode, | ||
143 | ) -> ResolvePathResult { | ||
107 | let mut segments = path.segments.iter().enumerate(); | 144 | let mut segments = path.segments.iter().enumerate(); |
108 | let mut curr_per_ns: PerNs = match path.kind { | 145 | let mut curr_per_ns: PerNs = match path.kind { |
109 | PathKind::DollarCrate(krate) => { | 146 | PathKind::DollarCrate(krate) => { |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 723481c36..73e3a4702 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -4,11 +4,13 @@ mod macros; | |||
4 | mod mod_resolution; | 4 | mod mod_resolution; |
5 | mod diagnostics; | 5 | mod diagnostics; |
6 | mod primitives; | 6 | mod primitives; |
7 | mod block; | ||
7 | 8 | ||
8 | use std::sync::Arc; | 9 | use std::sync::Arc; |
9 | 10 | ||
10 | use base_db::{fixture::WithFixture, SourceDatabase}; | 11 | use base_db::{fixture::WithFixture, SourceDatabase}; |
11 | use expect_test::{expect, Expect}; | 12 | use expect_test::{expect, Expect}; |
13 | use hir_expand::db::AstDatabase; | ||
12 | use test_utils::mark; | 14 | use test_utils::mark; |
13 | 15 | ||
14 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; | 16 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; |
@@ -19,12 +21,30 @@ fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { | |||
19 | db.crate_def_map(krate) | 21 | db.crate_def_map(krate) |
20 | } | 22 | } |
21 | 23 | ||
24 | fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> { | ||
25 | let (db, position) = TestDB::with_position(ra_fixture); | ||
26 | let module = db.module_for_file(position.file_id); | ||
27 | let ast_map = db.ast_id_map(position.file_id.into()); | ||
28 | let ast = db.parse(position.file_id); | ||
29 | let block: ast::BlockExpr = | ||
30 | syntax::algo::find_node_at_offset(&ast.syntax_node(), position.offset).unwrap(); | ||
31 | let block_id = ast_map.ast_id(&block); | ||
32 | |||
33 | db.block_def_map(module.krate, InFile::new(position.file_id.into(), block_id)) | ||
34 | } | ||
35 | |||
22 | fn check(ra_fixture: &str, expect: Expect) { | 36 | fn check(ra_fixture: &str, expect: Expect) { |
23 | let def_map = compute_crate_def_map(ra_fixture); | 37 | let def_map = compute_crate_def_map(ra_fixture); |
24 | let actual = def_map.dump(); | 38 | let actual = def_map.dump(); |
25 | expect.assert_eq(&actual); | 39 | expect.assert_eq(&actual); |
26 | } | 40 | } |
27 | 41 | ||
42 | fn check_at(ra_fixture: &str, expect: Expect) { | ||
43 | let def_map = compute_block_def_map(ra_fixture); | ||
44 | let actual = def_map.dump(); | ||
45 | expect.assert_eq(&actual); | ||
46 | } | ||
47 | |||
28 | #[test] | 48 | #[test] |
29 | fn crate_def_map_smoke_test() { | 49 | fn crate_def_map_smoke_test() { |
30 | check( | 50 | check( |
diff --git a/crates/hir_def/src/nameres/tests/block.rs b/crates/hir_def/src/nameres/tests/block.rs new file mode 100644 index 000000000..ab7ec9d62 --- /dev/null +++ b/crates/hir_def/src/nameres/tests/block.rs | |||
@@ -0,0 +1,72 @@ | |||
1 | use super::*; | ||
2 | |||
3 | #[test] | ||
4 | fn inner_item_smoke() { | ||
5 | check_at( | ||
6 | r#" | ||
7 | //- /lib.rs | ||
8 | struct inner {} | ||
9 | fn outer() { | ||
10 | $0 | ||
11 | fn inner() {} | ||
12 | } | ||
13 | "#, | ||
14 | expect![[r#" | ||
15 | block scope | ||
16 | inner: v | ||
17 | crate | ||
18 | inner: t | ||
19 | outer: v | ||
20 | "#]], | ||
21 | ); | ||
22 | } | ||
23 | |||
24 | #[test] | ||
25 | fn use_from_crate() { | ||
26 | check_at( | ||
27 | r#" | ||
28 | //- /lib.rs | ||
29 | struct Struct; | ||
30 | fn outer() { | ||
31 | use Struct; | ||
32 | use crate::Struct as CrateStruct; | ||
33 | use self::Struct as SelfStruct; | ||
34 | $0 | ||
35 | } | ||
36 | "#, | ||
37 | expect![[r#" | ||
38 | block scope | ||
39 | CrateStruct: t v | ||
40 | SelfStruct: t v | ||
41 | Struct: t v | ||
42 | crate | ||
43 | Struct: t v | ||
44 | outer: v | ||
45 | "#]], | ||
46 | ); | ||
47 | } | ||
48 | |||
49 | #[test] | ||
50 | fn merge_namespaces() { | ||
51 | check_at( | ||
52 | r#" | ||
53 | //- /lib.rs | ||
54 | struct name {} | ||
55 | fn outer() { | ||
56 | fn name() {} | ||
57 | |||
58 | use name as imported; // should import both `name`s | ||
59 | |||
60 | $0 | ||
61 | } | ||
62 | "#, | ||
63 | expect![[r#" | ||
64 | block scope | ||
65 | imported: t v | ||
66 | name: v | ||
67 | crate | ||
68 | name: t | ||
69 | outer: v | ||
70 | "#]], | ||
71 | ); | ||
72 | } | ||