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.rs166
1 files changed, 28 insertions, 138 deletions
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs
index dcec51a10..476385a2f 100644
--- a/crates/ra_hir/src/expr/scope.rs
+++ b/crates/ra_hir/src/expr/scope.rs
@@ -1,17 +1,16 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap};
4
5use ra_syntax::{ 4use ra_syntax::{
6 AstNode, SyntaxNode, TextUnit, TextRange, SyntaxNodePtr, AstPtr, 5 TextRange, AstPtr,
7 algo::generate, 6 algo::generate,
8 ast, 7 ast,
9}; 8};
10use ra_arena::{Arena, RawId, impl_arena_id}; 9use ra_arena::{Arena, RawId, impl_arena_id};
11 10
12use crate::{ 11use crate::{
13 Name, AsName,DefWithBody, Either, 12 Name, DefWithBody, Either,
14 expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySourceMap}, 13 expr::{PatId, ExprId, Pat, Expr, Body, Statement},
15 HirDatabase, 14 HirDatabase,
16}; 15};
17 16
@@ -23,7 +22,7 @@ impl_arena_id!(ScopeId);
23pub struct ExprScopes { 22pub struct ExprScopes {
24 body: Arc<Body>, 23 body: Arc<Body>,
25 scopes: Arena<ScopeId, ScopeData>, 24 scopes: Arena<ScopeId, ScopeData>,
26 scope_for: FxHashMap<ExprId, ScopeId>, 25 pub(crate) scope_for: FxHashMap<ExprId, ScopeId>,
27} 26}
28 27
29#[derive(Debug, PartialEq, Eq)] 28#[derive(Debug, PartialEq, Eq)]
@@ -66,10 +65,7 @@ impl ExprScopes {
66 &self.scopes[scope].entries 65 &self.scopes[scope].entries
67 } 66 }
68 67
69 pub fn scope_chain_for<'a>( 68 pub fn scope_chain<'a>(&'a self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + 'a {
70 &'a self,
71 scope: Option<ScopeId>,
72 ) -> impl Iterator<Item = ScopeId> + 'a {
73 generate(scope, move |&scope| self.scopes[scope].parent) 69 generate(scope, move |&scope| self.scopes[scope].parent)
74 } 70 }
75 71
@@ -108,15 +104,9 @@ impl ExprScopes {
108} 104}
109 105
110#[derive(Debug, Clone, PartialEq, Eq)] 106#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct ScopesWithSourceMap {
112 pub(crate) source_map: Arc<BodySourceMap>,
113 pub(crate) scopes: Arc<ExprScopes>,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct ScopeEntryWithSyntax { 107pub struct ScopeEntryWithSyntax {
118 name: Name, 108 pub(crate) name: Name,
119 ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, 109 pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
120} 110}
121 111
122impl ScopeEntryWithSyntax { 112impl ScopeEntryWithSyntax {
@@ -129,96 +119,6 @@ impl ScopeEntryWithSyntax {
129 } 119 }
130} 120}
131 121
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(crate) 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(crate) fn resolve_local_name(
177 &self,
178 name_ref: &ast::NameRef,
179 ) -> Option<ScopeEntryWithSyntax> {
180 let mut shadowed = FxHashSet::default();
181 let name = name_ref.as_name();
182 let ret = self
183 .scope_chain(name_ref.syntax())
184 .flat_map(|scope| self.scopes.entries(scope).iter())
185 .filter(|entry| shadowed.insert(entry.name()))
186 .filter(|entry| entry.name() == &name)
187 .nth(0);
188 ret.and_then(|entry| {
189 Some(ScopeEntryWithSyntax {
190 name: entry.name().clone(),
191 ptr: self.source_map.pat_syntax(entry.pat())?,
192 })
193 })
194 }
195
196 pub(crate) fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
197 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
198 let ptr = Either::A(AstPtr::new(pat.into()));
199 fn_def
200 .syntax()
201 .descendants()
202 .filter_map(ast::NameRef::cast)
203 .filter(|name_ref| match self.resolve_local_name(*name_ref) {
204 None => false,
205 Some(entry) => entry.ptr() == ptr,
206 })
207 .map(|name_ref| ReferenceDescriptor {
208 name: name_ref.syntax().text().to_string(),
209 range: name_ref.syntax().range(),
210 })
211 .collect()
212 }
213
214 pub(crate) fn scope_for(&self, node: &SyntaxNode) -> Option<ScopeId> {
215 node.ancestors()
216 .map(SyntaxNodePtr::new)
217 .filter_map(|ptr| self.source_map.syntax_expr(ptr))
218 .find_map(|it| self.scopes.scope_for(it))
219 }
220}
221
222impl ScopeEntry { 122impl ScopeEntry {
223 pub fn name(&self) -> &Name { 123 pub fn name(&self) -> &Name {
224 &self.name 124 &self.name
@@ -297,12 +197,11 @@ pub struct ReferenceDescriptor {
297 197
298#[cfg(test)] 198#[cfg(test)]
299mod tests { 199mod tests {
300 use ra_db::salsa::InternKey; 200 use ra_db::SourceDatabase;
301 use ra_syntax::{SourceFile, algo::find_node_at_offset}; 201 use ra_syntax::{algo::find_node_at_offset, AstNode, SyntaxNodePtr};
302 use test_utils::{extract_offset, assert_eq_text}; 202 use test_utils::{extract_offset, assert_eq_text};
303 use crate::Function;
304 203
305 use crate::expr::{ExprCollector}; 204 use crate::{source_binder::SourceAnalyzer, mock::MockDatabase};
306 205
307 use super::*; 206 use super::*;
308 207
@@ -316,18 +215,20 @@ mod tests {
316 buf.push_str(&code[off..]); 215 buf.push_str(&code[off..]);
317 buf 216 buf
318 }; 217 };
319 let file = SourceFile::parse(&code); 218
219 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
220 let file = db.parse(file_id);
320 let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 221 let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
321 let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 222 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
322 let irrelevant_function = 223
323 Function { id: crate::ids::FunctionId::from_intern_id(0u32.into()) }; 224 let scopes = analyzer.scopes();
324 let (body, source_map) = collect_fn_body_syntax(irrelevant_function, fn_def); 225 let expr_id =
325 let scopes = ExprScopes::new(Arc::new(body)); 226 analyzer.body_source_map().syntax_expr(SyntaxNodePtr::new(marker.syntax())).unwrap();
326 let scopes = 227 let scope = scopes.scope_for(expr_id);
327 ScopesWithSourceMap { scopes: Arc::new(scopes), source_map: Arc::new(source_map) }; 228
328 let actual = scopes 229 let actual = scopes
329 .scope_chain(marker.syntax()) 230 .scope_chain(scope)
330 .flat_map(|scope| scopes.scopes.entries(scope)) 231 .flat_map(|scope| scopes.entries(scope))
331 .map(|it| it.name().to_string()) 232 .map(|it| it.name().to_string())
332 .collect::<Vec<_>>() 233 .collect::<Vec<_>>()
333 .join("\n"); 234 .join("\n");
@@ -410,28 +311,17 @@ mod tests {
410 ); 311 );
411 } 312 }
412 313
413 fn collect_fn_body_syntax(function: Function, node: &ast::FnDef) -> (Body, BodySourceMap) {
414 let mut collector = ExprCollector::new(DefWithBody::Function(function));
415 collector.collect_fn_body(node);
416 collector.finish()
417 }
418
419 fn do_check_local_name(code: &str, expected_offset: u32) { 314 fn do_check_local_name(code: &str, expected_offset: u32) {
420 let (off, code) = extract_offset(code); 315 let (off, code) = extract_offset(code);
421 let file = SourceFile::parse(&code); 316
317 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
318 let file = db.parse(file_id);
422 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) 319 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
423 .expect("failed to find a name at the target offset"); 320 .expect("failed to find a name at the target offset");
424
425 let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
426 let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); 321 let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
322 let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);
427 323
428 let irrelevant_function = 324 let local_name_entry = analyzer.resolve_local_name(name_ref).unwrap();
429 Function { id: crate::ids::FunctionId::from_intern_id(0u32.into()) };
430 let (body, source_map) = collect_fn_body_syntax(irrelevant_function, fn_def);
431 let scopes = ExprScopes::new(Arc::new(body));
432 let scopes =
433 ScopesWithSourceMap { scopes: Arc::new(scopes), source_map: Arc::new(source_map) };
434 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
435 let local_name = 325 let local_name =
436 local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); 326 local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
437 assert_eq!(local_name.range(), expected_name.syntax().range()); 327 assert_eq!(local_name.range(), expected_name.syntax().range());