diff options
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 162 |
1 files changed, 125 insertions, 37 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 1c9e9320d..d87f8ff34 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -7,9 +7,10 @@ | |||
7 | /// purely for "IDE needs". | 7 | /// purely for "IDE needs". |
8 | use std::sync::Arc; | 8 | use std::sync::Arc; |
9 | 9 | ||
10 | use rustc_hash::FxHashSet; | ||
10 | use ra_db::{FileId, FilePosition}; | 11 | use ra_db::{FileId, FilePosition}; |
11 | use ra_syntax::{ | 12 | use ra_syntax::{ |
12 | SyntaxNode, AstPtr, TextUnit, | 13 | SyntaxNode, AstPtr, TextUnit, SyntaxNodePtr, |
13 | ast::{self, AstNode, NameOwner}, | 14 | ast::{self, AstNode, NameOwner}, |
14 | algo::find_node_at_offset, | 15 | algo::find_node_at_offset, |
15 | SyntaxKind::*, | 16 | SyntaxKind::*, |
@@ -18,7 +19,7 @@ use ra_syntax::{ | |||
18 | use crate::{ | 19 | use crate::{ |
19 | HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, | 20 | HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, |
20 | AsName, Module, HirFileId, Crate, Trait, Resolver, | 21 | AsName, Module, HirFileId, Crate, Trait, Resolver, |
21 | expr::scope::{ReferenceDescriptor, ScopeEntryWithSyntax}, | 22 | expr::{BodySourceMap, scope::{ReferenceDescriptor, ScopeEntryWithSyntax, ScopeId, ExprScopes}}, |
22 | ids::LocationCtx, | 23 | ids::LocationCtx, |
23 | expr, AstId | 24 | expr, AstId |
24 | }; | 25 | }; |
@@ -120,29 +121,6 @@ pub fn trait_from_module( | |||
120 | Trait { id: ctx.to_def(trait_def) } | 121 | Trait { id: ctx.to_def(trait_def) } |
121 | } | 122 | } |
122 | 123 | ||
123 | fn resolver_for_node( | ||
124 | db: &impl HirDatabase, | ||
125 | file_id: FileId, | ||
126 | node: &SyntaxNode, | ||
127 | offset: Option<TextUnit>, | ||
128 | ) -> Resolver { | ||
129 | node.ancestors() | ||
130 | .find_map(|node| { | ||
131 | if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { | ||
132 | let def = def_with_body_from_child_node(db, file_id, node)?; | ||
133 | let scopes = def.scopes(db); | ||
134 | let scope = match offset { | ||
135 | None => scopes.scope_for(&node), | ||
136 | Some(offset) => scopes.scope_for_offset(offset), | ||
137 | }; | ||
138 | Some(expr::resolver_for_scope(def.body(db), db, scope)) | ||
139 | } else { | ||
140 | try_get_resolver_for_node(db, file_id, node) | ||
141 | } | ||
142 | }) | ||
143 | .unwrap_or_default() | ||
144 | } | ||
145 | |||
146 | fn try_get_resolver_for_node( | 124 | fn try_get_resolver_for_node( |
147 | db: &impl HirDatabase, | 125 | db: &impl HirDatabase, |
148 | file_id: FileId, | 126 | file_id: FileId, |
@@ -192,9 +170,9 @@ fn def_with_body_from_child_node( | |||
192 | #[derive(Debug)] | 170 | #[derive(Debug)] |
193 | pub struct SourceAnalyzer { | 171 | pub struct SourceAnalyzer { |
194 | resolver: Resolver, | 172 | resolver: Resolver, |
195 | body_source_map: Option<Arc<crate::expr::BodySourceMap>>, | 173 | body_source_map: Option<Arc<BodySourceMap>>, |
196 | infer: Option<Arc<crate::ty::InferenceResult>>, | 174 | infer: Option<Arc<crate::ty::InferenceResult>>, |
197 | scopes: Option<crate::expr::ScopesWithSourceMap>, | 175 | scopes: Option<Arc<crate::expr::ExprScopes>>, |
198 | } | 176 | } |
199 | 177 | ||
200 | #[derive(Debug, Clone, PartialEq, Eq)] | 178 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -217,11 +195,30 @@ impl SourceAnalyzer { | |||
217 | offset: Option<TextUnit>, | 195 | offset: Option<TextUnit>, |
218 | ) -> SourceAnalyzer { | 196 | ) -> SourceAnalyzer { |
219 | let def_with_body = def_with_body_from_child_node(db, file_id, node); | 197 | let def_with_body = def_with_body_from_child_node(db, file_id, node); |
220 | SourceAnalyzer { | 198 | if let Some(def) = def_with_body { |
221 | resolver: resolver_for_node(db, file_id, node, offset), | 199 | let source_map = def.body_source_map(db); |
222 | body_source_map: def_with_body.map(|it| it.body_source_map(db)), | 200 | let scopes = db.expr_scopes(def); |
223 | infer: def_with_body.map(|it| it.infer(db)), | 201 | let scope = match offset { |
224 | scopes: def_with_body.map(|it| it.scopes(db)), | 202 | None => scope_for(&scopes, &source_map, &node), |
203 | Some(offset) => scope_for_offset(&scopes, &source_map, offset), | ||
204 | }; | ||
205 | let resolver = expr::resolver_for_scope(def.body(db), db, scope); | ||
206 | SourceAnalyzer { | ||
207 | resolver, | ||
208 | body_source_map: Some(source_map), | ||
209 | infer: Some(def.infer(db)), | ||
210 | scopes: Some(scopes), | ||
211 | } | ||
212 | } else { | ||
213 | SourceAnalyzer { | ||
214 | resolver: node | ||
215 | .ancestors() | ||
216 | .find_map(|node| try_get_resolver_for_node(db, file_id, node)) | ||
217 | .unwrap_or_default(), | ||
218 | body_source_map: None, | ||
219 | infer: None, | ||
220 | scopes: None, | ||
221 | } | ||
225 | } | 222 | } |
226 | } | 223 | } |
227 | 224 | ||
@@ -276,16 +273,46 @@ impl SourceAnalyzer { | |||
276 | Some(res) | 273 | Some(res) |
277 | } | 274 | } |
278 | 275 | ||
279 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Option<Vec<ReferenceDescriptor>> { | 276 | pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { |
280 | self.scopes.as_ref().map(|it| it.find_all_refs(pat)) | 277 | let mut shadowed = FxHashSet::default(); |
278 | let name = name_ref.as_name(); | ||
279 | let source_map = self.body_source_map.as_ref()?; | ||
280 | let scopes = self.scopes.as_ref()?; | ||
281 | let scope = scope_for(scopes, source_map, name_ref.syntax()); | ||
282 | let ret = scopes | ||
283 | .scope_chain(scope) | ||
284 | .flat_map(|scope| scopes.entries(scope).iter()) | ||
285 | .filter(|entry| shadowed.insert(entry.name())) | ||
286 | .filter(|entry| entry.name() == &name) | ||
287 | .nth(0); | ||
288 | ret.and_then(|entry| { | ||
289 | Some(ScopeEntryWithSyntax { | ||
290 | name: entry.name().clone(), | ||
291 | ptr: source_map.pat_syntax(entry.pat())?, | ||
292 | }) | ||
293 | }) | ||
281 | } | 294 | } |
282 | 295 | ||
283 | pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> { | 296 | pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> { |
284 | self.scopes.as_ref()?.resolve_local_name(name_ref) | 297 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); |
298 | let ptr = Either::A(AstPtr::new(pat.into())); | ||
299 | fn_def | ||
300 | .syntax() | ||
301 | .descendants() | ||
302 | .filter_map(ast::NameRef::cast) | ||
303 | .filter(|name_ref| match self.resolve_local_name(*name_ref) { | ||
304 | None => false, | ||
305 | Some(entry) => entry.ptr() == ptr, | ||
306 | }) | ||
307 | .map(|name_ref| ReferenceDescriptor { | ||
308 | name: name_ref.syntax().text().to_string(), | ||
309 | range: name_ref.syntax().range(), | ||
310 | }) | ||
311 | .collect() | ||
285 | } | 312 | } |
286 | 313 | ||
287 | #[cfg(test)] | 314 | #[cfg(test)] |
288 | pub(crate) fn body_source_map(&self) -> Arc<crate::expr::BodySourceMap> { | 315 | pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> { |
289 | self.body_source_map.clone().unwrap() | 316 | self.body_source_map.clone().unwrap() |
290 | } | 317 | } |
291 | 318 | ||
@@ -293,4 +320,65 @@ impl SourceAnalyzer { | |||
293 | pub(crate) fn inference_result(&self) -> Arc<crate::ty::InferenceResult> { | 320 | pub(crate) fn inference_result(&self) -> Arc<crate::ty::InferenceResult> { |
294 | self.infer.clone().unwrap() | 321 | self.infer.clone().unwrap() |
295 | } | 322 | } |
323 | |||
324 | #[cfg(test)] | ||
325 | pub(crate) fn scopes(&self) -> Arc<ExprScopes> { | ||
326 | self.scopes.clone().unwrap() | ||
327 | } | ||
328 | } | ||
329 | |||
330 | fn scope_for( | ||
331 | scopes: &ExprScopes, | ||
332 | source_map: &BodySourceMap, | ||
333 | node: &SyntaxNode, | ||
334 | ) -> Option<ScopeId> { | ||
335 | node.ancestors() | ||
336 | .map(SyntaxNodePtr::new) | ||
337 | .filter_map(|ptr| source_map.syntax_expr(ptr)) | ||
338 | .find_map(|it| scopes.scope_for(it)) | ||
339 | } | ||
340 | |||
341 | fn scope_for_offset( | ||
342 | scopes: &ExprScopes, | ||
343 | source_map: &BodySourceMap, | ||
344 | offset: TextUnit, | ||
345 | ) -> Option<ScopeId> { | ||
346 | scopes | ||
347 | .scope_for | ||
348 | .iter() | ||
349 | .filter_map(|(id, scope)| Some((source_map.expr_syntax(*id)?, scope))) | ||
350 | // find containing scope | ||
351 | .min_by_key(|(ptr, _scope)| { | ||
352 | (!(ptr.range().start() <= offset && offset <= ptr.range().end()), ptr.range().len()) | ||
353 | }) | ||
354 | .map(|(ptr, scope)| adjust(scopes, source_map, ptr, offset).unwrap_or(*scope)) | ||
355 | } | ||
356 | |||
357 | // XXX: during completion, cursor might be outside of any particular | ||
358 | // expression. Try to figure out the correct scope... | ||
359 | fn adjust( | ||
360 | scopes: &ExprScopes, | ||
361 | source_map: &BodySourceMap, | ||
362 | ptr: SyntaxNodePtr, | ||
363 | offset: TextUnit, | ||
364 | ) -> Option<ScopeId> { | ||
365 | let r = ptr.range(); | ||
366 | let child_scopes = scopes | ||
367 | .scope_for | ||
368 | .iter() | ||
369 | .filter_map(|(id, scope)| Some((source_map.expr_syntax(*id)?, scope))) | ||
370 | .map(|(ptr, scope)| (ptr.range(), scope)) | ||
371 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); | ||
372 | |||
373 | child_scopes | ||
374 | .max_by(|(r1, _), (r2, _)| { | ||
375 | if r2.is_subrange(&r1) { | ||
376 | std::cmp::Ordering::Greater | ||
377 | } else if r1.is_subrange(&r2) { | ||
378 | std::cmp::Ordering::Less | ||
379 | } else { | ||
380 | r1.start().cmp(&r2.start()) | ||
381 | } | ||
382 | }) | ||
383 | .map(|(_ptr, scope)| *scope) | ||
296 | } | 384 | } |