diff options
Diffstat (limited to 'crates/ra_editor/src/scope/fn_scope.rs')
-rw-r--r-- | crates/ra_editor/src/scope/fn_scope.rs | 150 |
1 files changed, 95 insertions, 55 deletions
diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index 99d698b60..9088e5a60 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs | |||
@@ -1,10 +1,11 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | |||
2 | use rustc_hash::FxHashMap; | 3 | use rustc_hash::FxHashMap; |
3 | 4 | ||
4 | use ra_syntax::{ | 5 | use ra_syntax::{ |
5 | SyntaxNodeRef, SyntaxNode, SmolStr, AstNode, | 6 | algo::generate, |
6 | ast::{self, NameOwner, LoopBodyOwner, ArgListOwner}, | 7 | ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, |
7 | algo::{generate} | 8 | AstNode, SmolStr, SyntaxNode, SyntaxNodeRef, |
8 | }; | 9 | }; |
9 | 10 | ||
10 | type ScopeId = usize; | 11 | type ScopeId = usize; |
@@ -19,11 +20,12 @@ pub struct FnScopes { | |||
19 | impl FnScopes { | 20 | impl FnScopes { |
20 | pub fn new(fn_def: ast::FnDef) -> FnScopes { | 21 | pub fn new(fn_def: ast::FnDef) -> FnScopes { |
21 | let mut scopes = FnScopes { | 22 | let mut scopes = FnScopes { |
22 | self_param: fn_def.param_list() | 23 | self_param: fn_def |
24 | .param_list() | ||
23 | .and_then(|it| it.self_param()) | 25 | .and_then(|it| it.self_param()) |
24 | .map(|it| it.syntax().owned()), | 26 | .map(|it| it.syntax().owned()), |
25 | scopes: Vec::new(), | 27 | scopes: Vec::new(), |
26 | scope_for: FxHashMap::default() | 28 | scope_for: FxHashMap::default(), |
27 | }; | 29 | }; |
28 | let root = scopes.root_scope(); | 30 | let root = scopes.root_scope(); |
29 | scopes.add_params_bindings(root, fn_def.param_list()); | 31 | scopes.add_params_bindings(root, fn_def.param_list()); |
@@ -35,27 +37,38 @@ impl FnScopes { | |||
35 | pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { | 37 | pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { |
36 | &self.scopes[scope].entries | 38 | &self.scopes[scope].entries |
37 | } | 39 | } |
38 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a { | 40 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { |
39 | generate(self.scope_for(node), move |&scope| self.scopes[scope].parent) | 41 | generate(self.scope_for(node), move |&scope| { |
42 | self.scopes[scope].parent | ||
43 | }) | ||
40 | } | 44 | } |
41 | fn root_scope(&mut self) -> ScopeId { | 45 | fn root_scope(&mut self) -> ScopeId { |
42 | let res = self.scopes.len(); | 46 | let res = self.scopes.len(); |
43 | self.scopes.push(ScopeData { parent: None, entries: vec![] }); | 47 | self.scopes.push(ScopeData { |
48 | parent: None, | ||
49 | entries: vec![], | ||
50 | }); | ||
44 | res | 51 | res |
45 | } | 52 | } |
46 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | 53 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { |
47 | let res = self.scopes.len(); | 54 | let res = self.scopes.len(); |
48 | self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] }); | 55 | self.scopes.push(ScopeData { |
56 | parent: Some(parent), | ||
57 | entries: vec![], | ||
58 | }); | ||
49 | res | 59 | res |
50 | } | 60 | } |
51 | fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) { | 61 | fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) { |
52 | let entries = pat.syntax().descendants() | 62 | let entries = pat |
63 | .syntax() | ||
64 | .descendants() | ||
53 | .filter_map(ast::BindPat::cast) | 65 | .filter_map(ast::BindPat::cast) |
54 | .filter_map(ScopeEntry::new); | 66 | .filter_map(ScopeEntry::new); |
55 | self.scopes[scope].entries.extend(entries); | 67 | self.scopes[scope].entries.extend(entries); |
56 | } | 68 | } |
57 | fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) { | 69 | fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) { |
58 | params.into_iter() | 70 | params |
71 | .into_iter() | ||
59 | .flat_map(|it| it.params()) | 72 | .flat_map(|it| it.params()) |
60 | .filter_map(|it| it.pat()) | 73 | .filter_map(|it| it.pat()) |
61 | .for_each(|it| self.add_bindings(scope, it)); | 74 | .for_each(|it| self.add_bindings(scope, it)); |
@@ -71,34 +84,33 @@ impl FnScopes { | |||
71 | } | 84 | } |
72 | 85 | ||
73 | pub struct ScopeEntry { | 86 | pub struct ScopeEntry { |
74 | syntax: SyntaxNode | 87 | syntax: SyntaxNode, |
75 | } | 88 | } |
76 | 89 | ||
77 | impl ScopeEntry { | 90 | impl ScopeEntry { |
78 | fn new(pat: ast::BindPat) -> Option<ScopeEntry> { | 91 | fn new(pat: ast::BindPat) -> Option<ScopeEntry> { |
79 | if pat.name().is_some() { | 92 | if pat.name().is_some() { |
80 | Some(ScopeEntry { syntax: pat.syntax().owned() }) | 93 | Some(ScopeEntry { |
94 | syntax: pat.syntax().owned(), | ||
95 | }) | ||
81 | } else { | 96 | } else { |
82 | None | 97 | None |
83 | } | 98 | } |
84 | } | 99 | } |
85 | pub fn name(&self) -> SmolStr { | 100 | pub fn name(&self) -> SmolStr { |
86 | self.ast().name() | 101 | self.ast().name().unwrap().text() |
87 | .unwrap() | ||
88 | .text() | ||
89 | } | 102 | } |
90 | pub fn ast(&self) -> ast::BindPat { | 103 | pub fn ast(&self) -> ast::BindPat { |
91 | ast::BindPat::cast(self.syntax.borrowed()) | 104 | ast::BindPat::cast(self.syntax.borrowed()).unwrap() |
92 | .unwrap() | ||
93 | } | 105 | } |
94 | } | 106 | } |
95 | 107 | ||
96 | impl fmt::Debug for ScopeEntry { | 108 | impl fmt::Debug for ScopeEntry { |
97 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 109 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
98 | f.debug_struct("ScopeEntry") | 110 | f.debug_struct("ScopeEntry") |
99 | .field("name", &self.name()) | 111 | .field("name", &self.name()) |
100 | .field("syntax", &self.syntax) | 112 | .field("syntax", &self.syntax) |
101 | .finish() | 113 | .finish() |
102 | } | 114 | } |
103 | } | 115 | } |
104 | 116 | ||
@@ -132,16 +144,16 @@ fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: Sco | |||
132 | fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | 144 | fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { |
133 | match expr { | 145 | match expr { |
134 | ast::Expr::IfExpr(e) => { | 146 | ast::Expr::IfExpr(e) => { |
135 | let cond_scope = e.condition().and_then(|cond| { | 147 | let cond_scope = e |
136 | compute_cond_scopes(cond, scopes, scope) | 148 | .condition() |
137 | }); | 149 | .and_then(|cond| compute_cond_scopes(cond, scopes, scope)); |
138 | if let Some(block) = e.then_branch() { | 150 | if let Some(block) = e.then_branch() { |
139 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | 151 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); |
140 | } | 152 | } |
141 | if let Some(block) = e.else_branch() { | 153 | if let Some(block) = e.else_branch() { |
142 | compute_block_scopes(block, scopes, scope); | 154 | compute_block_scopes(block, scopes, scope); |
143 | } | 155 | } |
144 | }, | 156 | } |
145 | ast::Expr::BlockExpr(e) => { | 157 | ast::Expr::BlockExpr(e) => { |
146 | if let Some(block) = e.block() { | 158 | if let Some(block) = e.block() { |
147 | compute_block_scopes(block, scopes, scope); | 159 | compute_block_scopes(block, scopes, scope); |
@@ -153,9 +165,9 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
153 | } | 165 | } |
154 | } | 166 | } |
155 | ast::Expr::WhileExpr(e) => { | 167 | ast::Expr::WhileExpr(e) => { |
156 | let cond_scope = e.condition().and_then(|cond| { | 168 | let cond_scope = e |
157 | compute_cond_scopes(cond, scopes, scope) | 169 | .condition() |
158 | }); | 170 | .and_then(|cond| compute_cond_scopes(cond, scopes, scope)); |
159 | if let Some(block) = e.loop_body() { | 171 | if let Some(block) = e.loop_body() { |
160 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | 172 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); |
161 | } | 173 | } |
@@ -201,25 +213,31 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
201 | } | 213 | } |
202 | } | 214 | } |
203 | } | 215 | } |
204 | _ => { | 216 | _ => expr |
205 | expr.syntax().children() | 217 | .syntax() |
206 | .filter_map(ast::Expr::cast) | 218 | .children() |
207 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)) | 219 | .filter_map(ast::Expr::cast) |
208 | } | 220 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)), |
209 | }; | 221 | }; |
210 | 222 | ||
211 | fn compute_call_scopes( | 223 | fn compute_call_scopes( |
212 | receiver: Option<ast::Expr>, | 224 | receiver: Option<ast::Expr>, |
213 | arg_list: Option<ast::ArgList>, | 225 | arg_list: Option<ast::ArgList>, |
214 | scopes: &mut FnScopes, scope: ScopeId, | 226 | scopes: &mut FnScopes, |
227 | scope: ScopeId, | ||
215 | ) { | 228 | ) { |
216 | arg_list.into_iter() | 229 | arg_list |
230 | .into_iter() | ||
217 | .flat_map(|it| it.args()) | 231 | .flat_map(|it| it.args()) |
218 | .chain(receiver) | 232 | .chain(receiver) |
219 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); | 233 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); |
220 | } | 234 | } |
221 | 235 | ||
222 | fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> { | 236 | fn compute_cond_scopes( |
237 | cond: ast::Condition, | ||
238 | scopes: &mut FnScopes, | ||
239 | scope: ScopeId, | ||
240 | ) -> Option<ScopeId> { | ||
223 | if let Some(expr) = cond.expr() { | 241 | if let Some(expr) = cond.expr() { |
224 | compute_expr_scopes(expr, scopes, scope); | 242 | compute_expr_scopes(expr, scopes, scope); |
225 | } | 243 | } |
@@ -236,14 +254,18 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
236 | #[derive(Debug)] | 254 | #[derive(Debug)] |
237 | struct ScopeData { | 255 | struct ScopeData { |
238 | parent: Option<ScopeId>, | 256 | parent: Option<ScopeId>, |
239 | entries: Vec<ScopeEntry> | 257 | entries: Vec<ScopeEntry>, |
240 | } | 258 | } |
241 | 259 | ||
242 | pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> Option<&'a ScopeEntry> { | 260 | pub fn resolve_local_name<'a>( |
261 | name_ref: ast::NameRef, | ||
262 | scopes: &'a FnScopes, | ||
263 | ) -> Option<&'a ScopeEntry> { | ||
243 | use rustc_hash::FxHashSet; | 264 | use rustc_hash::FxHashSet; |
244 | 265 | ||
245 | let mut shadowed = FxHashSet::default(); | 266 | let mut shadowed = FxHashSet::default(); |
246 | let ret = scopes.scope_chain(name_ref.syntax()) | 267 | let ret = scopes |
268 | .scope_chain(name_ref.syntax()) | ||
247 | .flat_map(|scope| scopes.entries(scope).iter()) | 269 | .flat_map(|scope| scopes.entries(scope).iter()) |
248 | .filter(|entry| shadowed.insert(entry.name())) | 270 | .filter(|entry| shadowed.insert(entry.name())) |
249 | .filter(|entry| entry.name() == name_ref.text()) | 271 | .filter(|entry| entry.name() == name_ref.text()) |
@@ -255,8 +277,8 @@ pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> O | |||
255 | #[cfg(test)] | 277 | #[cfg(test)] |
256 | mod tests { | 278 | mod tests { |
257 | use super::*; | 279 | use super::*; |
258 | use ra_syntax::File; | ||
259 | use crate::{find_node_at_offset, test_utils::extract_offset}; | 280 | use crate::{find_node_at_offset, test_utils::extract_offset}; |
281 | use ra_syntax::File; | ||
260 | 282 | ||
261 | fn do_check(code: &str, expected: &[&str]) { | 283 | fn do_check(code: &str, expected: &[&str]) { |
262 | let (off, code) = extract_offset(code); | 284 | let (off, code) = extract_offset(code); |
@@ -272,7 +294,8 @@ mod tests { | |||
272 | let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); | 294 | let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); |
273 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); | 295 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); |
274 | let scopes = FnScopes::new(fn_def); | 296 | let scopes = FnScopes::new(fn_def); |
275 | let actual = scopes.scope_chain(marker.syntax()) | 297 | let actual = scopes |
298 | .scope_chain(marker.syntax()) | ||
276 | .flat_map(|scope| scopes.entries(scope)) | 299 | .flat_map(|scope| scopes.entries(scope)) |
277 | .map(|it| it.name()) | 300 | .map(|it| it.name()) |
278 | .collect::<Vec<_>>(); | 301 | .collect::<Vec<_>>(); |
@@ -281,7 +304,8 @@ mod tests { | |||
281 | 304 | ||
282 | #[test] | 305 | #[test] |
283 | fn test_lambda_scope() { | 306 | fn test_lambda_scope() { |
284 | do_check(r" | 307 | do_check( |
308 | r" | ||
285 | fn quux(foo: i32) { | 309 | fn quux(foo: i32) { |
286 | let f = |bar, baz: i32| { | 310 | let f = |bar, baz: i32| { |
287 | <|> | 311 | <|> |
@@ -293,7 +317,8 @@ mod tests { | |||
293 | 317 | ||
294 | #[test] | 318 | #[test] |
295 | fn test_call_scope() { | 319 | fn test_call_scope() { |
296 | do_check(r" | 320 | do_check( |
321 | r" | ||
297 | fn quux() { | 322 | fn quux() { |
298 | f(|x| <|> ); | 323 | f(|x| <|> ); |
299 | }", | 324 | }", |
@@ -303,7 +328,8 @@ mod tests { | |||
303 | 328 | ||
304 | #[test] | 329 | #[test] |
305 | fn test_metod_call_scope() { | 330 | fn test_metod_call_scope() { |
306 | do_check(r" | 331 | do_check( |
332 | r" | ||
307 | fn quux() { | 333 | fn quux() { |
308 | z.f(|x| <|> ); | 334 | z.f(|x| <|> ); |
309 | }", | 335 | }", |
@@ -313,7 +339,8 @@ mod tests { | |||
313 | 339 | ||
314 | #[test] | 340 | #[test] |
315 | fn test_loop_scope() { | 341 | fn test_loop_scope() { |
316 | do_check(r" | 342 | do_check( |
343 | r" | ||
317 | fn quux() { | 344 | fn quux() { |
318 | loop { | 345 | loop { |
319 | let x = (); | 346 | let x = (); |
@@ -326,7 +353,8 @@ mod tests { | |||
326 | 353 | ||
327 | #[test] | 354 | #[test] |
328 | fn test_match() { | 355 | fn test_match() { |
329 | do_check(r" | 356 | do_check( |
357 | r" | ||
330 | fn quux() { | 358 | fn quux() { |
331 | match () { | 359 | match () { |
332 | Some(x) => { | 360 | Some(x) => { |
@@ -340,7 +368,8 @@ mod tests { | |||
340 | 368 | ||
341 | #[test] | 369 | #[test] |
342 | fn test_shadow_variable() { | 370 | fn test_shadow_variable() { |
343 | do_check(r" | 371 | do_check( |
372 | r" | ||
344 | fn foo(x: String) { | 373 | fn foo(x: String) { |
345 | let x : &str = &x<|>; | 374 | let x : &str = &x<|>; |
346 | }", | 375 | }", |
@@ -356,14 +385,20 @@ mod tests { | |||
356 | 385 | ||
357 | let scopes = FnScopes::new(fn_def); | 386 | let scopes = FnScopes::new(fn_def); |
358 | 387 | ||
359 | let local_name = resolve_local_name(name_ref, &scopes).unwrap().ast().name().unwrap(); | 388 | let local_name = resolve_local_name(name_ref, &scopes) |
360 | let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | 389 | .unwrap() |
390 | .ast() | ||
391 | .name() | ||
392 | .unwrap(); | ||
393 | let expected_name = | ||
394 | find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | ||
361 | assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); | 395 | assert_eq!(local_name.syntax().range(), expected_name.syntax().range()); |
362 | } | 396 | } |
363 | 397 | ||
364 | #[test] | 398 | #[test] |
365 | fn test_resolve_local_name() { | 399 | fn test_resolve_local_name() { |
366 | do_check_local_name(r#" | 400 | do_check_local_name( |
401 | r#" | ||
367 | fn foo(x: i32, y: u32) { | 402 | fn foo(x: i32, y: u32) { |
368 | { | 403 | { |
369 | let z = x * 2; | 404 | let z = x * 2; |
@@ -372,25 +407,30 @@ mod tests { | |||
372 | let t = x<|> * 3; | 407 | let t = x<|> * 3; |
373 | } | 408 | } |
374 | }"#, | 409 | }"#, |
375 | 21); | 410 | 21, |
411 | ); | ||
376 | } | 412 | } |
377 | 413 | ||
378 | #[test] | 414 | #[test] |
379 | fn test_resolve_local_name_declaration() { | 415 | fn test_resolve_local_name_declaration() { |
380 | do_check_local_name(r#" | 416 | do_check_local_name( |
417 | r#" | ||
381 | fn foo(x: String) { | 418 | fn foo(x: String) { |
382 | let x : &str = &x<|>; | 419 | let x : &str = &x<|>; |
383 | }"#, | 420 | }"#, |
384 | 21); | 421 | 21, |
422 | ); | ||
385 | } | 423 | } |
386 | 424 | ||
387 | #[test] | 425 | #[test] |
388 | fn test_resolve_local_name_shadow() { | 426 | fn test_resolve_local_name_shadow() { |
389 | do_check_local_name(r" | 427 | do_check_local_name( |
428 | r" | ||
390 | fn foo(x: String) { | 429 | fn foo(x: String) { |
391 | let x : &str = &x; | 430 | let x : &str = &x; |
392 | x<|> | 431 | x<|> |
393 | }", | 432 | }", |
394 | 46); | 433 | 46, |
434 | ); | ||
395 | } | 435 | } |
396 | } | 436 | } |