aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/expr/scope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/expr/scope.rs')
-rw-r--r--crates/ra_hir/src/expr/scope.rs232
1 files changed, 49 insertions, 183 deletions
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs
index 725b6c00e..58f365128 100644
--- a/crates/ra_hir/src/expr/scope.rs
+++ b/crates/ra_hir/src/expr/scope.rs
@@ -1,17 +1,11 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::FxHashMap;
4
5use ra_syntax::{
6 AstNode, SyntaxNode, TextUnit, TextRange, SyntaxNodePtr, AstPtr,
7 algo::generate,
8 ast,
9};
10use ra_arena::{Arena, RawId, impl_arena_id}; 4use ra_arena::{Arena, RawId, impl_arena_id};
11 5
12use crate::{ 6use crate::{
13 Name, AsName,DefWithBody, Either, 7 Name, DefWithBody,
14 expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySourceMap}, 8 expr::{PatId, ExprId, Pat, Expr, Body, Statement},
15 HirDatabase, 9 HirDatabase,
16}; 10};
17 11
@@ -23,23 +17,32 @@ impl_arena_id!(ScopeId);
23pub struct ExprScopes { 17pub struct ExprScopes {
24 body: Arc<Body>, 18 body: Arc<Body>,
25 scopes: Arena<ScopeId, ScopeData>, 19 scopes: Arena<ScopeId, ScopeData>,
26 scope_for: FxHashMap<ExprId, ScopeId>, 20 scope_by_expr: FxHashMap<ExprId, ScopeId>,
27} 21}
28 22
29#[derive(Debug, PartialEq, Eq)] 23#[derive(Debug, PartialEq, Eq)]
30pub struct ScopeEntry { 24pub(crate) struct ScopeEntry {
31 name: Name, 25 name: Name,
32 pat: PatId, 26 pat: PatId,
33} 27}
34 28
29impl ScopeEntry {
30 pub(crate) fn name(&self) -> &Name {
31 &self.name
32 }
33
34 pub(crate) fn pat(&self) -> PatId {
35 self.pat
36 }
37}
38
35#[derive(Debug, PartialEq, Eq)] 39#[derive(Debug, PartialEq, Eq)]
36pub struct ScopeData { 40pub(crate) struct ScopeData {
37 parent: Option<ScopeId>, 41 parent: Option<ScopeId>,
38 entries: Vec<ScopeEntry>, 42 entries: Vec<ScopeEntry>,
39} 43}
40 44
41impl ExprScopes { 45impl ExprScopes {
42 // FIXME: This should take something more general than Function
43 pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<ExprScopes> { 46 pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<ExprScopes> {
44 let body = db.body_hir(def); 47 let body = db.body_hir(def);
45 let res = ExprScopes::new(body); 48 let res = ExprScopes::new(body);
@@ -50,7 +53,7 @@ impl ExprScopes {
50 let mut scopes = ExprScopes { 53 let mut scopes = ExprScopes {
51 body: body.clone(), 54 body: body.clone(),
52 scopes: Arena::default(), 55 scopes: Arena::default(),
53 scope_for: FxHashMap::default(), 56 scope_by_expr: FxHashMap::default(),
54 }; 57 };
55 let root = scopes.root_scope(); 58 let root = scopes.root_scope();
56 scopes.add_params_bindings(root, body.params()); 59 scopes.add_params_bindings(root, body.params());
@@ -58,19 +61,23 @@ impl ExprScopes {
58 scopes 61 scopes
59 } 62 }
60 63
61 pub fn body(&self) -> Arc<Body> { 64 pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
62 self.body.clone()
63 }
64
65 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
66 &self.scopes[scope].entries 65 &self.scopes[scope].entries
67 } 66 }
68 67
69 pub fn scope_chain_for<'a>( 68 pub(crate) fn scope_chain<'a>(
70 &'a self, 69 &'a self,
71 scope: Option<ScopeId>, 70 scope: Option<ScopeId>,
72 ) -> impl Iterator<Item = ScopeId> + 'a { 71 ) -> impl Iterator<Item = ScopeId> + 'a {
73 generate(scope, move |&scope| self.scopes[scope].parent) 72 std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
73 }
74
75 pub(crate) fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
76 self.scope_by_expr.get(&expr).map(|&scope| scope)
77 }
78
79 pub(crate) fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> {
80 &self.scope_by_expr
74 } 81 }
75 82
76 fn root_scope(&mut self) -> ScopeId { 83 fn root_scope(&mut self) -> ScopeId {
@@ -99,130 +106,7 @@ impl ExprScopes {
99 } 106 }
100 107
101 fn set_scope(&mut self, node: ExprId, scope: ScopeId) { 108 fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
102 self.scope_for.insert(node, scope); 109 self.scope_by_expr.insert(node, scope);
103 }
104
105 pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
106 self.scope_for.get(&expr).map(|&scope| scope)
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct ScopesWithSourceMap {
112 pub source_map: Arc<BodySourceMap>,
113 pub scopes: Arc<ExprScopes>,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct ScopeEntryWithSyntax {
118 name: Name,
119 ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
120}
121
122impl ScopeEntryWithSyntax {
123 pub fn name(&self) -> &Name {
124 &self.name
125 }
126
127 pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> {
128 self.ptr
129 }
130}
131
132impl ScopesWithSourceMap {
133 fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator<Item = ScopeId> + 'a {
134 generate(self.scope_for(node), move |&scope| self.scopes.scopes[scope].parent)
135 }
136
137 pub fn scope_for_offset(&self, offset: TextUnit) -> Option<ScopeId> {
138 self.scopes
139 .scope_for
140 .iter()
141 .filter_map(|(id, scope)| Some((self.source_map.expr_syntax(*id)?, scope)))
142 // find containing scope
143 .min_by_key(|(ptr, _scope)| {
144 (!(ptr.range().start() <= offset && offset <= ptr.range().end()), ptr.range().len())
145 })
146 .map(|(ptr, scope)| self.adjust(ptr, *scope, offset))
147 }
148
149 // XXX: during completion, cursor might be outside of any particular
150 // expression. Try to figure out the correct scope...
151 // FIXME: move this to source binder?
152 fn adjust(&self, ptr: SyntaxNodePtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
153 let r = ptr.range();
154 let child_scopes = self
155 .scopes
156 .scope_for
157 .iter()
158 .filter_map(|(id, scope)| Some((self.source_map.expr_syntax(*id)?, scope)))
159 .map(|(ptr, scope)| (ptr.range(), scope))
160 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
161
162 child_scopes
163 .max_by(|(r1, _), (r2, _)| {
164 if r2.is_subrange(&r1) {
165 std::cmp::Ordering::Greater
166 } else if r1.is_subrange(&r2) {
167 std::cmp::Ordering::Less
168 } else {
169 r1.start().cmp(&r2.start())
170 }
171 })
172 .map(|(_ptr, scope)| *scope)
173 .unwrap_or(original_scope)
174 }
175
176 pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> {
177 let mut shadowed = FxHashSet::default();
178 let name = name_ref.as_name();
179 let ret = self
180 .scope_chain(name_ref.syntax())
181 .flat_map(|scope| self.scopes.entries(scope).iter())
182 .filter(|entry| shadowed.insert(entry.name()))
183 .filter(|entry| entry.name() == &name)
184 .nth(0);
185 ret.and_then(|entry| {
186 Some(ScopeEntryWithSyntax {
187 name: entry.name().clone(),
188 ptr: self.source_map.pat_syntax(entry.pat())?,
189 })
190 })
191 }
192
193 pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
194 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
195 let ptr = Either::A(AstPtr::new(pat.into()));
196 fn_def
197 .syntax()
198 .descendants()
199 .filter_map(ast::NameRef::cast)
200 .filter(|name_ref| match self.resolve_local_name(*name_ref) {
201 None => false,
202 Some(entry) => entry.ptr() == ptr,
203 })
204 .map(|name_ref| ReferenceDescriptor {
205 name: name_ref.syntax().text().to_string(),
206 range: name_ref.syntax().range(),
207 })
208 .collect()
209 }
210
211 pub fn scope_for(&self, node: &SyntaxNode) -> Option<ScopeId> {
212 node.ancestors()
213 .map(SyntaxNodePtr::new)
214 .filter_map(|ptr| self.source_map.syntax_expr(ptr))
215 .find_map(|it| self.scopes.scope_for(it))
216 }
217}
218
219impl ScopeEntry {
220 pub fn name(&self) -> &Name {
221 &self.name
222 }
223
224 pub fn pat(&self) -> PatId {
225 self.pat
226 } 110 }
227} 111}
228 112
@@ -286,22 +170,13 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
286 }; 170 };
287} 171}
288 172
289#[derive(Debug)]
290pub struct ReferenceDescriptor {
291 pub range: TextRange,
292 pub name: String,
293}
294
295#[cfg(test)] 173#[cfg(test)]
296mod tests { 174mod tests {
297 use ra_db::salsa::InternKey; 175 use ra_db::SourceDatabase;
298 use ra_syntax::{SourceFile, algo::find_node_at_offset}; 176 use ra_syntax::{algo::find_node_at_offset, AstNode, SyntaxNodePtr, ast};
299 use test_utils::{extract_offset, assert_eq_text}; 177 use test_utils::{extract_offset, assert_eq_text};
300 use crate::Function;
301
302 use crate::expr::{ExprCollector};
303 178
304 use super::*; 179 use crate::{source_binder::SourceAnalyzer, mock::MockDatabase};
305 180
306 fn do_check(code: &str, expected: &[&str]) { 181 fn do_check(code: &str, expected: &[&str]) {
307 let (off, code) = extract_offset(code); 182 let (off, code) = extract_offset(code);
@@ -313,18 +188,20 @@ mod tests {
313 buf.push_str(&code[off..]); 188 buf.push_str(&code[off..]);
314 buf 189 buf
315 }; 190 };
316 let file = SourceFile::parse(&code); 191
192 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
193 let file = db.parse(file_id);
317 let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 194 let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
318 let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 195 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
319 let irrelevant_function = 196
320 Function { id: crate::ids::FunctionId::from_intern_id(0u32.into()) }; 197 let scopes = analyzer.scopes();
321 let (body, source_map) = collect_fn_body_syntax(irrelevant_function, fn_def); 198 let expr_id =
322 let scopes = ExprScopes::new(Arc::new(body)); 199 analyzer.body_source_map().syntax_expr(SyntaxNodePtr::new(marker.syntax())).unwrap();
323 let scopes = 200 let scope = scopes.scope_for(expr_id);
324 ScopesWithSourceMap { scopes: Arc::new(scopes), source_map: Arc::new(source_map) }; 201
325 let actual = scopes 202 let actual = scopes
326 .scope_chain(marker.syntax()) 203 .scope_chain(scope)
327 .flat_map(|scope| scopes.scopes.entries(scope)) 204 .flat_map(|scope| scopes.entries(scope))
328 .map(|it| it.name().to_string()) 205 .map(|it| it.name().to_string())
329 .collect::<Vec<_>>() 206 .collect::<Vec<_>>()
330 .join("\n"); 207 .join("\n");
@@ -407,28 +284,17 @@ mod tests {
407 ); 284 );
408 } 285 }
409 286
410 fn collect_fn_body_syntax(function: Function, node: &ast::FnDef) -> (Body, BodySourceMap) {
411 let mut collector = ExprCollector::new(DefWithBody::Function(function));
412 collector.collect_fn_body(node);
413 collector.finish()
414 }
415
416 fn do_check_local_name(code: &str, expected_offset: u32) { 287 fn do_check_local_name(code: &str, expected_offset: u32) {
417 let (off, code) = extract_offset(code); 288 let (off, code) = extract_offset(code);
418 let file = SourceFile::parse(&code); 289
290 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
291 let file = db.parse(file_id);
419 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) 292 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
420 .expect("failed to find a name at the target offset"); 293 .expect("failed to find a name at the target offset");
421
422 let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
423 let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); 294 let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
295 let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);
424 296
425 let irrelevant_function = 297 let local_name_entry = analyzer.resolve_local_name(name_ref).unwrap();
426 Function { id: crate::ids::FunctionId::from_intern_id(0u32.into()) };
427 let (body, source_map) = collect_fn_body_syntax(irrelevant_function, fn_def);
428 let scopes = ExprScopes::new(Arc::new(body));
429 let scopes =
430 ScopesWithSourceMap { scopes: Arc::new(scopes), source_map: Arc::new(source_map) };
431 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
432 let local_name = 298 let local_name =
433 local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); 299 local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
434 assert_eq!(local_name.range(), expected_name.syntax().range()); 300 assert_eq!(local_name.range(), expected_name.syntax().range());