aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-02-09 16:22:57 +0000
committerJonas Schievink <[email protected]>2021-02-09 16:22:57 +0000
commit27f77060e2452fda5c2896c9b46feaa8399c3eae (patch)
treee146374a0edc198b5b297bc1f021bee00f990ad7 /crates
parent1956286368809718c70692e422893559ec487c62 (diff)
Add `TestDB::module_at_position`
Diffstat (limited to 'crates')
-rw-r--r--crates/hir_def/src/body/tests.rs104
-rw-r--r--crates/hir_def/src/test_db.rs99
2 files changed, 99 insertions, 104 deletions
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/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();