aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor/src/scope/fn_scope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libeditor/src/scope/fn_scope.rs')
-rw-r--r--crates/libeditor/src/scope/fn_scope.rs83
1 files changed, 75 insertions, 8 deletions
diff --git a/crates/libeditor/src/scope/fn_scope.rs b/crates/libeditor/src/scope/fn_scope.rs
index e807c6c71..a38647c43 100644
--- a/crates/libeditor/src/scope/fn_scope.rs
+++ b/crates/libeditor/src/scope/fn_scope.rs
@@ -24,11 +24,7 @@ impl FnScopes {
24 scope_for: HashMap::new() 24 scope_for: HashMap::new()
25 }; 25 };
26 let root = scopes.root_scope(); 26 let root = scopes.root_scope();
27 fn_def.param_list().into_iter() 27 scopes.add_params_bindings(root, fn_def.param_list());
28 .flat_map(|it| it.params())
29 .filter_map(|it| it.pat())
30 .for_each(|it| scopes.add_bindings(root, it));
31
32 if let Some(body) = fn_def.body() { 28 if let Some(body) = fn_def.body() {
33 compute_block_scopes(body, &mut scopes, root) 29 compute_block_scopes(body, &mut scopes, root)
34 } 30 }
@@ -56,6 +52,12 @@ impl FnScopes {
56 .filter_map(ScopeEntry::new); 52 .filter_map(ScopeEntry::new);
57 self.scopes[scope].entries.extend(entries); 53 self.scopes[scope].entries.extend(entries);
58 } 54 }
55 fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
56 params.into_iter()
57 .flat_map(|it| it.params())
58 .filter_map(|it| it.pat())
59 .for_each(|it| self.add_bindings(scope, it));
60 }
59 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) { 61 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
60 self.scope_for.insert(node.owned(), scope); 62 self.scope_for.insert(node.owned(), scope);
61 } 63 }
@@ -102,13 +104,14 @@ fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: Sco
102 for stmt in block.statements() { 104 for stmt in block.statements() {
103 match stmt { 105 match stmt {
104 ast::Stmt::LetStmt(stmt) => { 106 ast::Stmt::LetStmt(stmt) => {
107 if let Some(expr) = stmt.initializer() {
108 scopes.set_scope(expr.syntax(), scope);
109 compute_expr_scopes(expr, scopes, scope);
110 }
105 scope = scopes.new_scope(scope); 111 scope = scopes.new_scope(scope);
106 if let Some(pat) = stmt.pat() { 112 if let Some(pat) = stmt.pat() {
107 scopes.add_bindings(scope, pat); 113 scopes.add_bindings(scope, pat);
108 } 114 }
109 if let Some(expr) = stmt.initializer() {
110 scopes.set_scope(expr.syntax(), scope)
111 }
112 } 115 }
113 ast::Stmt::ExprStmt(expr_stmt) => { 116 ast::Stmt::ExprStmt(expr_stmt) => {
114 if let Some(expr) = expr_stmt.expr() { 117 if let Some(expr) = expr_stmt.expr() {
@@ -163,6 +166,20 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
163 compute_block_scopes(block, scopes, scope); 166 compute_block_scopes(block, scopes, scope);
164 } 167 }
165 }, 168 },
169 ast::Expr::LambdaExpr(e) => {
170 let mut scope = scopes.new_scope(scope);
171 scopes.add_params_bindings(scope, e.param_list());
172 if let Some(body) = e.body() {
173 scopes.set_scope(body.syntax(), scope);
174 compute_expr_scopes(body, scopes, scope);
175 }
176 }
177 ast::Expr::CallExpr(e) => {
178 e.arg_list().into_iter()
179 .flat_map(|it| it.args())
180 .chain(e.expr())
181 .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
182 }
166 _ => { 183 _ => {
167 expr.syntax().children() 184 expr.syntax().children()
168 .filter_map(ast::Expr::cast) 185 .filter_map(ast::Expr::cast)
@@ -189,3 +206,53 @@ struct ScopeData {
189 parent: Option<ScopeId>, 206 parent: Option<ScopeId>,
190 entries: Vec<ScopeEntry> 207 entries: Vec<ScopeEntry>
191} 208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use libsyntax2::File;
214 use {find_node_at_offset, test_utils::extract_offset};
215
216 fn do_check(code: &str, expected: &[&str]) {
217 let (off, code) = extract_offset(code);
218 let code = {
219 let mut buf = String::new();
220 let off = u32::from(off) as usize;
221 buf.push_str(&code[..off]);
222 buf.push_str("marker");
223 buf.push_str(&code[off..]);
224 buf
225 };
226 let file = File::parse(&code);
227 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
228 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
229 let scopes = FnScopes::new(fn_def);
230 let actual = scopes.scope_chain(marker.syntax())
231 .flat_map(|scope| scopes.entries(scope))
232 .map(|it| it.name())
233 .collect::<Vec<_>>();
234 assert_eq!(expected, actual.as_slice());
235 }
236
237 #[test]
238 fn test_lambda_scope() {
239 do_check(r"
240 fn quux(foo: i32) {
241 let f = |bar| {
242 <|>
243 };
244 }",
245 &["bar", "foo"],
246 );
247 }
248
249 #[test]
250 fn test_call_scope() {
251 do_check(r"
252 fn quux() {
253 f(|x| <|> );
254 }",
255 &["x"],
256 );
257 }
258}