Skip to content

Commit

Permalink
Merge #98
Browse files Browse the repository at this point in the history
98: WIP: Add resolve_local_name to resolve names in a function scope r=kjeremy a=kjeremy

First step to resolving #80 

Co-authored-by: Jeremy A. Kolb <jkolb@ara.com>
  • Loading branch information
bors[bot] and kjeremy committed Oct 7, 2018
2 parents f53c8ae + ff1b2da commit e4fdfd1
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 7 deletions.
18 changes: 16 additions & 2 deletions crates/ra_analysis/src/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*,
Expand Down Expand Up @@ -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::<ast::NameRef>(syntax, offset) {
return self.index_resolve(name_ref, token);

// First try to resolve the symbol locally
if let Some((name, range)) = resolve_local_name(&file, offset, name_ref) {
let mut vec = vec![];
vec.push((file_id, FileSymbol {
name,
node_range: 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::<ast::Name>(syntax, offset) {
if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
Expand Down
10 changes: 9 additions & 1 deletion crates/ra_editor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, *},
Expand Down Expand Up @@ -164,6 +164,14 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>(
.next()
}

pub fn resolve_local_name(file: &File, offset: TextUnit, name_ref: ast::NameRef) -> Option<(SmolStr, TextRange)> {
let fn_def = find_node_at_offset::<ast::FnDef>(file.syntax(), offset)?;
let scopes = scope::FnScopes::new(fn_def);
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)]
mod tests {
use super::*;
Expand Down
74 changes: 71 additions & 3 deletions crates/ra_editor/src/scope/fn_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down Expand Up @@ -241,6 +241,17 @@ struct ScopeData {
entries: Vec<ScopeEntry>
}

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();
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)
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -265,7 +276,7 @@ mod tests {
.flat_map(|scope| scopes.entries(scope))
.map(|it| it.name())
.collect::<Vec<_>>();
assert_eq!(expected, actual.as_slice());
assert_eq!(actual.as_slice(), expected);
}

#[test]
Expand Down Expand Up @@ -326,4 +337,61 @@ 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 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().ast().name().unwrap();

let expected_name = find_node_at_offset::<ast::Name>(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);
}
}
2 changes: 1 addition & 1 deletion crates/ra_editor/src/scope/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

0 comments on commit e4fdfd1

Please sign in to comment.