diff options
-rw-r--r-- | crates/ra_editor/src/scope/fn_scope.rs | 81 |
1 files changed, 79 insertions, 2 deletions
diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index eddd87495..03f9df094 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs | |||
@@ -241,6 +241,18 @@ 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<ast::Name<'a>> { | ||
245 | use std::collections::HashSet; | ||
246 | |||
247 | let mut shadowed = HashSet::new(); | ||
248 | let names = 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 | names.ast().name() | ||
254 | } | ||
255 | |||
244 | #[cfg(test)] | 256 | #[cfg(test)] |
245 | mod tests { | 257 | mod tests { |
246 | use super::*; | 258 | use super::*; |
@@ -265,7 +277,7 @@ mod tests { | |||
265 | .flat_map(|scope| scopes.entries(scope)) | 277 | .flat_map(|scope| scopes.entries(scope)) |
266 | .map(|it| it.name()) | 278 | .map(|it| it.name()) |
267 | .collect::<Vec<_>>(); | 279 | .collect::<Vec<_>>(); |
268 | assert_eq!(expected, actual.as_slice()); | 280 | assert_eq!(actual.as_slice(), expected); |
269 | } | 281 | } |
270 | 282 | ||
271 | #[test] | 283 | #[test] |
@@ -326,4 +338,69 @@ mod tests { | |||
326 | &["x"], | 338 | &["x"], |
327 | ); | 339 | ); |
328 | } | 340 | } |
329 | } | 341 | |
342 | #[test] | ||
343 | fn test_shadow_variable() { | ||
344 | do_check(r" | ||
345 | fn foo(x: String) { | ||
346 | let x : &str = &x<|>; | ||
347 | }", | ||
348 | &["x"], | ||
349 | ); | ||
350 | } | ||
351 | |||
352 | fn do_check_local_name(code: &str, expected_offset: u32) { | ||
353 | let (off, code) = extract_offset(code); | ||
354 | let code = { | ||
355 | let mut buf = String::new(); | ||
356 | let off = u32::from(off) as usize; | ||
357 | buf.push_str(&code[..off]); | ||
358 | buf.push_str(&code[off..]); | ||
359 | buf | ||
360 | }; | ||
361 | |||
362 | let file = File::parse(&code); | ||
363 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); | ||
364 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); | ||
365 | |||
366 | let scopes = FnScopes::new(fn_def); | ||
367 | |||
368 | let local_name = resolve_local_name(name_ref, &scopes).unwrap(); | ||
369 | |||
370 | let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | ||
371 | assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); | ||
372 | } | ||
373 | |||
374 | #[test] | ||
375 | fn test_resolve_local_name() { | ||
376 | do_check_local_name(r#" | ||
377 | fn foo(x: i32, y: u32) { | ||
378 | { | ||
379 | let z = x * 2; | ||
380 | } | ||
381 | { | ||
382 | let t = x<|> * 3; | ||
383 | } | ||
384 | }"#, | ||
385 | 21); | ||
386 | } | ||
387 | |||
388 | #[test] | ||
389 | fn test_resolve_local_name_declaration() { | ||
390 | do_check_local_name(r#" | ||
391 | fn foo(x: String) { | ||
392 | let x : &str = &x<|>; | ||
393 | }"#, | ||
394 | 21); | ||
395 | } | ||
396 | |||
397 | #[test] | ||
398 | fn test_resolve_local_name_shadow() { | ||
399 | do_check_local_name(r" | ||
400 | fn foo(x: String) { | ||
401 | let x : &str = &x; | ||
402 | x<|> | ||
403 | }", | ||
404 | 46); | ||
405 | } | ||
406 | } \ No newline at end of file | ||