aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/src/db.rs7
-rw-r--r--crates/hir_def/src/item_tree.rs7
-rw-r--r--crates/hir_def/src/item_tree/lower.rs38
-rw-r--r--crates/hir_def/src/nameres.rs95
-rw-r--r--crates/hir_def/src/nameres/collector.rs58
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs37
-rw-r--r--crates/hir_def/src/nameres/tests.rs20
-rw-r--r--crates/hir_def/src/nameres/tests/block.rs72
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 @@
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use base_db::{salsa, CrateId, SourceDatabase, Upcast}; 4use base_db::{salsa, CrateId, SourceDatabase, Upcast};
5use hir_expand::{db::AstDatabase, HirFileId}; 5use hir_expand::{db::AstDatabase, AstId, HirFileId};
6use la_arena::ArenaMap; 6use la_arena::ArenaMap;
7use syntax::SmolStr; 7use syntax::{ast, SmolStr};
8 8
9use crate::{ 9use 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};
61use la_arena::Arena; 61use la_arena::Arena;
62use rustc_hash::FxHashMap; 62use rustc_hash::FxHashMap;
63use stdx::format_to; 63use stdx::format_to;
64use syntax::ast; 64use syntax::{ast, AstNode};
65 65
66use crate::{ 66use 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)]
77pub struct DefMap { 77pub 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) = &current_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
338fn 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)]
307pub enum ModuleSource { 368pub 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;
45const EXPANSION_DEPTH_LIMIT: usize = 128; 45const EXPANSION_DEPTH_LIMIT: usize = 128;
46const FIXED_POINT_LIMIT: usize = 8192; 46const FIXED_POINT_LIMIT: usize = 8192;
47 47
48pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap { 48pub(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
230impl DefCollector<'_> { 242impl 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<'_, '_> {
1470mod tests { 1506mod 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 &current_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;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics; 5mod diagnostics;
6mod primitives; 6mod primitives;
7mod block;
7 8
8use std::sync::Arc; 9use std::sync::Arc;
9 10
10use base_db::{fixture::WithFixture, SourceDatabase}; 11use base_db::{fixture::WithFixture, SourceDatabase};
11use expect_test::{expect, Expect}; 12use expect_test::{expect, Expect};
13use hir_expand::db::AstDatabase;
12use test_utils::mark; 14use test_utils::mark;
13 15
14use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; 16use 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
24fn 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
22fn check(ra_fixture: &str, expect: Expect) { 36fn 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
42fn 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]
29fn crate_def_map_smoke_test() { 49fn 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 @@
1use super::*;
2
3#[test]
4fn inner_item_smoke() {
5 check_at(
6 r#"
7//- /lib.rs
8struct inner {}
9fn 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]
25fn use_from_crate() {
26 check_at(
27 r#"
28//- /lib.rs
29struct Struct;
30fn 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]
50fn merge_namespaces() {
51 check_at(
52 r#"
53//- /lib.rs
54struct name {}
55fn 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}