diff options
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 18 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 10 | ||||
-rw-r--r-- | crates/ra_editor/src/scope/fn_scope.rs | 74 | ||||
-rw-r--r-- | crates/ra_editor/src/scope/mod.rs | 2 |
4 files changed, 97 insertions, 7 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 90184a4b9..517867e86 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -9,7 +9,7 @@ use std::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use relative_path::RelativePath; | 11 | use relative_path::RelativePath; |
12 | use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit}; | 12 | use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name}; |
13 | use ra_syntax::{ | 13 | use ra_syntax::{ |
14 | TextUnit, TextRange, SmolStr, File, AstNode, | 14 | TextUnit, TextRange, SmolStr, File, AstNode, |
15 | SyntaxKind::*, | 15 | SyntaxKind::*, |
@@ -197,7 +197,21 @@ impl AnalysisImpl { | |||
197 | let file = root.syntax(file_id); | 197 | let file = root.syntax(file_id); |
198 | let syntax = file.syntax(); | 198 | let syntax = file.syntax(); |
199 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) { | 199 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) { |
200 | return self.index_resolve(name_ref, token); | 200 | |
201 | // First try to resolve the symbol locally | ||
202 | if let Some((name, range)) = resolve_local_name(&file, offset, name_ref) { | ||
203 | let mut vec = vec![]; | ||
204 | vec.push((file_id, FileSymbol { | ||
205 | name, | ||
206 | node_range: range, | ||
207 | kind : NAME | ||
208 | })); | ||
209 | |||
210 | return vec; | ||
211 | } else { | ||
212 | // If that fails try the index based approach. | ||
213 | return self.index_resolve(name_ref, token); | ||
214 | } | ||
201 | } | 215 | } |
202 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) { | 216 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) { |
203 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | 217 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { |
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index a93924e00..2a801f7da 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -19,7 +19,7 @@ mod scope; | |||
19 | mod test_utils; | 19 | mod test_utils; |
20 | 20 | ||
21 | use ra_syntax::{ | 21 | use ra_syntax::{ |
22 | File, TextUnit, TextRange, SyntaxNodeRef, | 22 | File, TextUnit, TextRange, SmolStr, SyntaxNodeRef, |
23 | ast::{self, AstNode, NameOwner}, | 23 | ast::{self, AstNode, NameOwner}, |
24 | algo::find_leaf_at_offset, | 24 | algo::find_leaf_at_offset, |
25 | SyntaxKind::{self, *}, | 25 | SyntaxKind::{self, *}, |
@@ -164,6 +164,14 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>( | |||
164 | .next() | 164 | .next() |
165 | } | 165 | } |
166 | 166 | ||
167 | pub fn resolve_local_name(file: &File, offset: TextUnit, name_ref: ast::NameRef) -> Option<(SmolStr, TextRange)> { | ||
168 | let fn_def = find_node_at_offset::<ast::FnDef>(file.syntax(), offset)?; | ||
169 | let scopes = scope::FnScopes::new(fn_def); | ||
170 | let scope_entry = scope::resolve_local_name(name_ref, &scopes)?; | ||
171 | let name = scope_entry.ast().name()?; | ||
172 | Some((scope_entry.name(), name.syntax().range())) | ||
173 | } | ||
174 | |||
167 | #[cfg(test)] | 175 | #[cfg(test)] |
168 | mod tests { | 176 | mod tests { |
169 | use super::*; | 177 | use super::*; |
diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index eddd87495..a99bd1822 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs | |||
@@ -89,7 +89,7 @@ impl ScopeEntry { | |||
89 | .unwrap() | 89 | .unwrap() |
90 | .text() | 90 | .text() |
91 | } | 91 | } |
92 | fn ast(&self) -> ast::BindPat { | 92 | pub fn ast(&self) -> ast::BindPat { |
93 | ast::BindPat::cast(self.syntax.borrowed()) | 93 | ast::BindPat::cast(self.syntax.borrowed()) |
94 | .unwrap() | 94 | .unwrap() |
95 | } | 95 | } |
@@ -241,6 +241,17 @@ struct ScopeData { | |||
241 | entries: Vec<ScopeEntry> | 241 | entries: Vec<ScopeEntry> |
242 | } | 242 | } |
243 | 243 | ||
244 | pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option<&'a ScopeEntry> { | ||
245 | use std::collections::HashSet; | ||
246 | |||
247 | let mut shadowed = HashSet::new(); | ||
248 | scopes.scope_chain(name_ref.syntax()) | ||
249 | .flat_map(|scope| scopes.entries(scope).iter()) | ||
250 | .filter(|entry| shadowed.insert(entry.name())) | ||
251 | .filter(|entry| entry.name() == name_ref.text()) | ||
252 | .nth(0) | ||
253 | } | ||
254 | |||
244 | #[cfg(test)] | 255 | #[cfg(test)] |
245 | mod tests { | 256 | mod tests { |
246 | use super::*; | 257 | use super::*; |
@@ -265,7 +276,7 @@ mod tests { | |||
265 | .flat_map(|scope| scopes.entries(scope)) | 276 | .flat_map(|scope| scopes.entries(scope)) |
266 | .map(|it| it.name()) | 277 | .map(|it| it.name()) |
267 | .collect::<Vec<_>>(); | 278 | .collect::<Vec<_>>(); |
268 | assert_eq!(expected, actual.as_slice()); | 279 | assert_eq!(actual.as_slice(), expected); |
269 | } | 280 | } |
270 | 281 | ||
271 | #[test] | 282 | #[test] |
@@ -326,4 +337,61 @@ mod tests { | |||
326 | &["x"], | 337 | &["x"], |
327 | ); | 338 | ); |
328 | } | 339 | } |
329 | } | 340 | |
341 | #[test] | ||
342 | fn test_shadow_variable() { | ||
343 | do_check(r" | ||
344 | fn foo(x: String) { | ||
345 | let x : &str = &x<|>; | ||
346 | }", | ||
347 | &["x"], | ||
348 | ); | ||
349 | } | ||
350 | |||
351 | fn do_check_local_name(code: &str, expected_offset: u32) { | ||
352 | let (off, code) = extract_offset(code); | ||
353 | let file = File::parse(&code); | ||
354 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); | ||
355 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); | ||
356 | |||
357 | let scopes = FnScopes::new(fn_def); | ||
358 | |||
359 | let local_name = resolve_local_name(name_ref, &scopes).unwrap().ast().name().unwrap(); | ||
360 | |||
361 | let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | ||
362 | assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); | ||
363 | } | ||
364 | |||
365 | #[test] | ||
366 | fn test_resolve_local_name() { | ||
367 | do_check_local_name(r#" | ||
368 | fn foo(x: i32, y: u32) { | ||
369 | { | ||
370 | let z = x * 2; | ||
371 | } | ||
372 | { | ||
373 | let t = x<|> * 3; | ||
374 | } | ||
375 | }"#, | ||
376 | 21); | ||
377 | } | ||
378 | |||
379 | #[test] | ||
380 | fn test_resolve_local_name_declaration() { | ||
381 | do_check_local_name(r#" | ||
382 | fn foo(x: String) { | ||
383 | let x : &str = &x<|>; | ||
384 | }"#, | ||
385 | 21); | ||
386 | } | ||
387 | |||
388 | #[test] | ||
389 | fn test_resolve_local_name_shadow() { | ||
390 | do_check_local_name(r" | ||
391 | fn foo(x: String) { | ||
392 | let x : &str = &x; | ||
393 | x<|> | ||
394 | }", | ||
395 | 46); | ||
396 | } | ||
397 | } \ No newline at end of file | ||
diff --git a/crates/ra_editor/src/scope/mod.rs b/crates/ra_editor/src/scope/mod.rs index 2f25230f8..7d6d530f7 100644 --- a/crates/ra_editor/src/scope/mod.rs +++ b/crates/ra_editor/src/scope/mod.rs | |||
@@ -2,7 +2,7 @@ mod fn_scope; | |||
2 | mod mod_scope; | 2 | mod mod_scope; |
3 | 3 | ||
4 | pub use self::{ | 4 | pub use self::{ |
5 | fn_scope::FnScopes, | 5 | fn_scope::{FnScopes, resolve_local_name}, |
6 | mod_scope::ModuleScope, | 6 | mod_scope::ModuleScope, |
7 | }; | 7 | }; |
8 | 8 | ||