From 91312a9ff98d1f82313c42a2387df49fbdf09ac6 Mon Sep 17 00:00:00 2001 From: "Jeremy A. Kolb" Date: Fri, 5 Oct 2018 10:53:17 -0400 Subject: Add resolve_local_name to resolve names in a function scope --- crates/ra_editor/src/scope/fn_scope.rs | 81 +++++++++++++++++++++++++++++++++- 1 file 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 { entries: Vec } +pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option> { + use std::collections::HashSet; + + let mut shadowed = HashSet::new(); + let names = scopes.scope_chain(name_ref.syntax()) + .flat_map(|scope| scopes.entries(scope).iter()) + .filter(|entry| shadowed.insert(entry.name())) + .filter(|entry| entry.name() == name_ref.text()) + .nth(0)?; + names.ast().name() +} + #[cfg(test)] mod tests { use super::*; @@ -265,7 +277,7 @@ mod tests { .flat_map(|scope| scopes.entries(scope)) .map(|it| it.name()) .collect::>(); - assert_eq!(expected, actual.as_slice()); + assert_eq!(actual.as_slice(), expected); } #[test] @@ -326,4 +338,69 @@ mod tests { &["x"], ); } -} + + #[test] + fn test_shadow_variable() { + do_check(r" + fn foo(x: String) { + let x : &str = &x<|>; + }", + &["x"], + ); + } + + fn do_check_local_name(code: &str, expected_offset: u32) { + let (off, code) = extract_offset(code); + let code = { + let mut buf = String::new(); + let off = u32::from(off) as usize; + buf.push_str(&code[..off]); + buf.push_str(&code[off..]); + buf + }; + + let file = File::parse(&code); + let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); + let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); + + let scopes = FnScopes::new(fn_def); + + let local_name = resolve_local_name(name_ref, &scopes).unwrap(); + + let expected_name = find_node_at_offset::(file.syntax(), expected_offset.into()).unwrap(); + assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); + } + + #[test] + fn test_resolve_local_name() { + do_check_local_name(r#" + fn foo(x: i32, y: u32) { + { + let z = x * 2; + } + { + let t = x<|> * 3; + } + }"#, + 21); + } + + #[test] + fn test_resolve_local_name_declaration() { + do_check_local_name(r#" + fn foo(x: String) { + let x : &str = &x<|>; + }"#, + 21); + } + + #[test] + fn test_resolve_local_name_shadow() { + do_check_local_name(r" + fn foo(x: String) { + let x : &str = &x; + x<|> + }", + 46); + } +} \ No newline at end of file -- cgit v1.2.3 From 1d4c767879913271bf01912f12c7f8f176c7755d Mon Sep 17 00:00:00 2001 From: "Jeremy A. Kolb" Date: Fri, 5 Oct 2018 15:21:40 -0400 Subject: WIP: This doesn't currently work but I also don't think it's the right abstraction --- crates/ra_analysis/src/imp.rs | 18 ++++++++++++++++-- crates/ra_editor/src/lib.rs | 8 ++++++++ crates/ra_editor/src/scope/mod.rs | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 90184a4b9..05c91fb83 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs @@ -9,7 +9,7 @@ use std::{ }; use relative_path::RelativePath; -use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit}; +use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name}; use ra_syntax::{ TextUnit, TextRange, SmolStr, File, AstNode, SyntaxKind::*, @@ -197,7 +197,21 @@ impl AnalysisImpl { let file = root.syntax(file_id); let syntax = file.syntax(); if let Some(name_ref) = find_node_at_offset::(syntax, offset) { - return self.index_resolve(name_ref, token); + + // First try to resolve the symbol locally + if let Some(name) = resolve_local_name(&file, offset, name_ref) { + let vec: Vec<(FileId, FileSymbol)>::new(); + vec.push((file_id, FileSymbol { + name: name.text(), + node_range: name.syntax().range(), + kind : NAME + })); + + return vec; + } else { + // If that fails try the index based approach. + return self.index_resolve(name_ref, token); + } } if let Some(name) = find_node_at_offset::(syntax, offset) { 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..fcb3e12e6 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs @@ -164,6 +164,14 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>( .next() } +pub fn resolve_local_name<'a>(file: &'a File, offset: TextUnit, name_ref: ast::NameRef) -> Option> { + let fn_def = find_node_at_offset::(file.syntax(), offset)?; + let scopes = scope::FnScopes::new(fn_def); + + // TODO: This doesn't work because of scopes lifetime + scope::resolve_local_name(name_ref, &scopes) +} + #[cfg(test)] mod tests { use super::*; 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; mod mod_scope; pub use self::{ - fn_scope::FnScopes, + fn_scope::{FnScopes, resolve_local_name}, mod_scope::ModuleScope, }; -- cgit v1.2.3 From 828bd73195a43dfba2837812c070880914001e8f Mon Sep 17 00:00:00 2001 From: "Jeremy A. Kolb" Date: Sat, 6 Oct 2018 12:02:15 -0400 Subject: Resolve local names first --- crates/ra_analysis/src/imp.rs | 8 ++++---- crates/ra_editor/src/lib.rs | 10 +++++----- crates/ra_editor/src/scope/fn_scope.rs | 11 +++++------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 05c91fb83..517867e86 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs @@ -199,11 +199,11 @@ impl AnalysisImpl { if let Some(name_ref) = find_node_at_offset::(syntax, offset) { // First try to resolve the symbol locally - if let Some(name) = resolve_local_name(&file, offset, name_ref) { - let vec: Vec<(FileId, FileSymbol)>::new(); + if let Some((name, range)) = resolve_local_name(&file, offset, name_ref) { + let mut vec = vec![]; vec.push((file_id, FileSymbol { - name: name.text(), - node_range: name.syntax().range(), + name, + node_range: range, kind : NAME })); diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index fcb3e12e6..2a801f7da 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs @@ -19,7 +19,7 @@ mod scope; mod test_utils; use ra_syntax::{ - File, TextUnit, TextRange, SyntaxNodeRef, + File, TextUnit, TextRange, SmolStr, SyntaxNodeRef, ast::{self, AstNode, NameOwner}, algo::find_leaf_at_offset, SyntaxKind::{self, *}, @@ -164,12 +164,12 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>( .next() } -pub fn resolve_local_name<'a>(file: &'a File, offset: TextUnit, name_ref: ast::NameRef) -> Option> { +pub fn resolve_local_name(file: &File, offset: TextUnit, name_ref: ast::NameRef) -> Option<(SmolStr, TextRange)> { let fn_def = find_node_at_offset::(file.syntax(), offset)?; let scopes = scope::FnScopes::new(fn_def); - - // TODO: This doesn't work because of scopes lifetime - scope::resolve_local_name(name_ref, &scopes) + let scope_entry = scope::resolve_local_name(name_ref, &scopes)?; + let name = scope_entry.ast().name()?; + Some((scope_entry.name(), name.syntax().range())) } #[cfg(test)] diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index 03f9df094..67eb8e2ab 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 { .unwrap() .text() } - fn ast(&self) -> ast::BindPat { + pub fn ast(&self) -> ast::BindPat { ast::BindPat::cast(self.syntax.borrowed()) .unwrap() } @@ -241,16 +241,15 @@ struct ScopeData { entries: Vec } -pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option> { +pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option<&'a ScopeEntry> { use std::collections::HashSet; let mut shadowed = HashSet::new(); - let names = scopes.scope_chain(name_ref.syntax()) + scopes.scope_chain(name_ref.syntax()) .flat_map(|scope| scopes.entries(scope).iter()) .filter(|entry| shadowed.insert(entry.name())) .filter(|entry| entry.name() == name_ref.text()) - .nth(0)?; - names.ast().name() + .nth(0) } #[cfg(test)] @@ -365,7 +364,7 @@ mod tests { let scopes = FnScopes::new(fn_def); - let local_name = resolve_local_name(name_ref, &scopes).unwrap(); + let local_name = resolve_local_name(name_ref, &scopes).unwrap().ast().name().unwrap(); let expected_name = find_node_at_offset::(file.syntax(), expected_offset.into()).unwrap(); assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); -- cgit v1.2.3 From ff1b2da50280ef40988ce79f8bb5e82aff7e68c5 Mon Sep 17 00:00:00 2001 From: "Jeremy A. Kolb" Date: Sat, 6 Oct 2018 13:41:33 -0400 Subject: Remove functional noop --- crates/ra_editor/src/scope/fn_scope.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index 67eb8e2ab..a99bd1822 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs @@ -350,14 +350,6 @@ mod tests { fn do_check_local_name(code: &str, expected_offset: u32) { let (off, code) = extract_offset(code); - let code = { - let mut buf = String::new(); - let off = u32::from(off) as usize; - buf.push_str(&code[..off]); - buf.push_str(&code[off..]); - buf - }; - let file = File::parse(&code); let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); -- cgit v1.2.3