diff options
Diffstat (limited to 'crates/libeditor/src/scope')
-rw-r--r-- | crates/libeditor/src/scope/fn_scope.rs | 83 |
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)] | ||
211 | mod 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 | } | ||