diff options
author | Aleksey Kladov <[email protected]> | 2019-04-13 08:49:01 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-04-13 08:49:01 +0100 |
commit | f4a94e74bcd6c8f9275a57a775e64314af1878da (patch) | |
tree | d10b8da727d6b581a78d79e660fe05218e5b80d3 /crates/ra_hir/src/expr | |
parent | 30481808fbfea109f324dfaf93daaaebacc75333 (diff) |
fold ScopeWithSyntax into SourceAnalyzer
Diffstat (limited to 'crates/ra_hir/src/expr')
-rw-r--r-- | crates/ra_hir/src/expr/scope.rs | 166 |
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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::{FxHashMap}; |
4 | |||
5 | use ra_syntax::{ | 4 | use ra_syntax::{ |
6 | AstNode, SyntaxNode, TextUnit, TextRange, SyntaxNodePtr, AstPtr, | 5 | TextRange, AstPtr, |
7 | algo::generate, | 6 | algo::generate, |
8 | ast, | 7 | ast, |
9 | }; | 8 | }; |
10 | use ra_arena::{Arena, RawId, impl_arena_id}; | 9 | use ra_arena::{Arena, RawId, impl_arena_id}; |
11 | 10 | ||
12 | use crate::{ | 11 | use 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); | |||
23 | pub struct ExprScopes { | 22 | pub 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)] |
111 | pub struct ScopesWithSourceMap { | ||
112 | pub(crate) source_map: Arc<BodySourceMap>, | ||
113 | pub(crate) scopes: Arc<ExprScopes>, | ||
114 | } | ||
115 | |||
116 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
117 | pub struct ScopeEntryWithSyntax { | 107 | pub 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 | ||
122 | impl ScopeEntryWithSyntax { | 112 | impl ScopeEntryWithSyntax { |
@@ -129,96 +119,6 @@ impl ScopeEntryWithSyntax { | |||
129 | } | 119 | } |
130 | } | 120 | } |
131 | 121 | ||
132 | impl 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 | |||
222 | impl ScopeEntry { | 122 | impl 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)] |
299 | mod tests { | 199 | mod 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()); |