aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/source_binder.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-04-13 08:49:01 +0100
committerAleksey Kladov <[email protected]>2019-04-13 08:49:01 +0100
commitf4a94e74bcd6c8f9275a57a775e64314af1878da (patch)
treed10b8da727d6b581a78d79e660fe05218e5b80d3 /crates/ra_hir/src/source_binder.rs
parent30481808fbfea109f324dfaf93daaaebacc75333 (diff)
fold ScopeWithSyntax into SourceAnalyzer
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r--crates/ra_hir/src/source_binder.rs162
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".
8use std::sync::Arc; 8use std::sync::Arc;
9 9
10use rustc_hash::FxHashSet;
10use ra_db::{FileId, FilePosition}; 11use ra_db::{FileId, FilePosition};
11use ra_syntax::{ 12use 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::{
18use crate::{ 19use 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
123fn 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
146fn try_get_resolver_for_node( 124fn 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)]
193pub struct SourceAnalyzer { 171pub 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
330fn 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
341fn 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...
359fn 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}