aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/hir/function/scope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/hir/function/scope.rs')
-rw-r--r--crates/ra_analysis/src/hir/function/scope.rs451
1 files changed, 0 insertions, 451 deletions
diff --git a/crates/ra_analysis/src/hir/function/scope.rs b/crates/ra_analysis/src/hir/function/scope.rs
deleted file mode 100644
index ed789fede..000000000
--- a/crates/ra_analysis/src/hir/function/scope.rs
+++ /dev/null
@@ -1,451 +0,0 @@
1use rustc_hash::{FxHashMap, FxHashSet};
2
3use ra_syntax::{
4 AstNode, SmolStr, SyntaxNodeRef, TextRange,
5 algo::generate,
6 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
7};
8use ra_db::LocalSyntaxPtr;
9
10use crate::{
11
12 arena::{Arena, Id},
13};
14
15pub(crate) type ScopeId = Id<ScopeData>;
16
17#[derive(Debug, PartialEq, Eq)]
18pub struct FnScopes {
19 pub(crate) self_param: Option<LocalSyntaxPtr>,
20 scopes: Arena<ScopeData>,
21 scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>,
22}
23
24#[derive(Debug, PartialEq, Eq)]
25pub struct ScopeEntry {
26 name: SmolStr,
27 ptr: LocalSyntaxPtr,
28}
29
30#[derive(Debug, PartialEq, Eq)]
31pub(crate) struct ScopeData {
32 parent: Option<ScopeId>,
33 entries: Vec<ScopeEntry>,
34}
35
36impl FnScopes {
37 pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes {
38 let mut scopes = FnScopes {
39 self_param: fn_def
40 .param_list()
41 .and_then(|it| it.self_param())
42 .map(|it| LocalSyntaxPtr::new(it.syntax())),
43 scopes: Arena::default(),
44 scope_for: FxHashMap::default(),
45 };
46 let root = scopes.root_scope();
47 scopes.add_params_bindings(root, fn_def.param_list());
48 if let Some(body) = fn_def.body() {
49 compute_block_scopes(body, &mut scopes, root)
50 }
51 scopes
52 }
53 pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
54 &self.scopes[scope].entries
55 }
56 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
57 generate(self.scope_for(node), move |&scope| {
58 self.scopes[scope].parent
59 })
60 }
61 pub(crate) fn resolve_local_name<'a>(
62 &'a self,
63 name_ref: ast::NameRef,
64 ) -> Option<&'a ScopeEntry> {
65 let mut shadowed = FxHashSet::default();
66 let ret = self
67 .scope_chain(name_ref.syntax())
68 .flat_map(|scope| self.entries(scope).iter())
69 .filter(|entry| shadowed.insert(entry.name()))
70 .filter(|entry| entry.name() == &name_ref.text())
71 .nth(0);
72 ret
73 }
74
75 pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> {
76 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
77 let name_ptr = LocalSyntaxPtr::new(pat.syntax());
78 let refs: Vec<_> = fn_def
79 .syntax()
80 .descendants()
81 .filter_map(ast::NameRef::cast)
82 .filter(|name_ref| match self.resolve_local_name(*name_ref) {
83 None => false,
84 Some(entry) => entry.ptr() == name_ptr,
85 })
86 .map(|name_ref| ReferenceDescriptor {
87 name: name_ref.syntax().text().to_string(),
88 range: name_ref.syntax().range(),
89 })
90 .collect();
91
92 refs
93 }
94
95 fn root_scope(&mut self) -> ScopeId {
96 self.scopes.alloc(ScopeData {
97 parent: None,
98 entries: vec![],
99 })
100 }
101 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
102 self.scopes.alloc(ScopeData {
103 parent: Some(parent),
104 entries: vec![],
105 })
106 }
107 fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
108 let entries = pat
109 .syntax()
110 .descendants()
111 .filter_map(ast::BindPat::cast)
112 .filter_map(ScopeEntry::new);
113 self.scopes[scope].entries.extend(entries);
114 }
115 fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
116 params
117 .into_iter()
118 .flat_map(|it| it.params())
119 .filter_map(|it| it.pat())
120 .for_each(|it| self.add_bindings(scope, it));
121 }
122 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
123 self.scope_for.insert(LocalSyntaxPtr::new(node), scope);
124 }
125 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
126 node.ancestors()
127 .map(LocalSyntaxPtr::new)
128 .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope))
129 .next()
130 }
131}
132
133impl ScopeEntry {
134 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
135 let name = pat.name()?;
136 let res = ScopeEntry {
137 name: name.text(),
138 ptr: LocalSyntaxPtr::new(pat.syntax()),
139 };
140 Some(res)
141 }
142 pub(crate) fn name(&self) -> &SmolStr {
143 &self.name
144 }
145 pub(crate) fn ptr(&self) -> LocalSyntaxPtr {
146 self.ptr
147 }
148}
149
150fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
151 for stmt in block.statements() {
152 match stmt {
153 ast::Stmt::LetStmt(stmt) => {
154 if let Some(expr) = stmt.initializer() {
155 scopes.set_scope(expr.syntax(), scope);
156 compute_expr_scopes(expr, scopes, scope);
157 }
158 scope = scopes.new_scope(scope);
159 if let Some(pat) = stmt.pat() {
160 scopes.add_bindings(scope, pat);
161 }
162 }
163 ast::Stmt::ExprStmt(expr_stmt) => {
164 if let Some(expr) = expr_stmt.expr() {
165 scopes.set_scope(expr.syntax(), scope);
166 compute_expr_scopes(expr, scopes, scope);
167 }
168 }
169 }
170 }
171 if let Some(expr) = block.expr() {
172 scopes.set_scope(expr.syntax(), scope);
173 compute_expr_scopes(expr, scopes, scope);
174 }
175}
176
177fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
178 match expr {
179 ast::Expr::IfExpr(e) => {
180 let cond_scope = e
181 .condition()
182 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
183 if let Some(block) = e.then_branch() {
184 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
185 }
186 if let Some(block) = e.else_branch() {
187 compute_block_scopes(block, scopes, scope);
188 }
189 }
190 ast::Expr::BlockExpr(e) => {
191 if let Some(block) = e.block() {
192 compute_block_scopes(block, scopes, scope);
193 }
194 }
195 ast::Expr::LoopExpr(e) => {
196 if let Some(block) = e.loop_body() {
197 compute_block_scopes(block, scopes, scope);
198 }
199 }
200 ast::Expr::WhileExpr(e) => {
201 let cond_scope = e
202 .condition()
203 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
204 if let Some(block) = e.loop_body() {
205 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
206 }
207 }
208 ast::Expr::ForExpr(e) => {
209 if let Some(expr) = e.iterable() {
210 compute_expr_scopes(expr, scopes, scope);
211 }
212 let mut scope = scope;
213 if let Some(pat) = e.pat() {
214 scope = scopes.new_scope(scope);
215 scopes.add_bindings(scope, pat);
216 }
217 if let Some(block) = e.loop_body() {
218 compute_block_scopes(block, scopes, scope);
219 }
220 }
221 ast::Expr::LambdaExpr(e) => {
222 let scope = scopes.new_scope(scope);
223 scopes.add_params_bindings(scope, e.param_list());
224 if let Some(body) = e.body() {
225 scopes.set_scope(body.syntax(), scope);
226 compute_expr_scopes(body, scopes, scope);
227 }
228 }
229 ast::Expr::CallExpr(e) => {
230 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
231 }
232 ast::Expr::MethodCallExpr(e) => {
233 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
234 }
235 ast::Expr::MatchExpr(e) => {
236 if let Some(expr) = e.expr() {
237 compute_expr_scopes(expr, scopes, scope);
238 }
239 for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
240 let scope = scopes.new_scope(scope);
241 for pat in arm.pats() {
242 scopes.add_bindings(scope, pat);
243 }
244 if let Some(expr) = arm.expr() {
245 compute_expr_scopes(expr, scopes, scope);
246 }
247 }
248 }
249 _ => expr
250 .syntax()
251 .children()
252 .filter_map(ast::Expr::cast)
253 .for_each(|expr| compute_expr_scopes(expr, scopes, scope)),
254 };
255
256 fn compute_call_scopes(
257 receiver: Option<ast::Expr>,
258 arg_list: Option<ast::ArgList>,
259 scopes: &mut FnScopes,
260 scope: ScopeId,
261 ) {
262 arg_list
263 .into_iter()
264 .flat_map(|it| it.args())
265 .chain(receiver)
266 .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
267 }
268
269 fn compute_cond_scopes(
270 cond: ast::Condition,
271 scopes: &mut FnScopes,
272 scope: ScopeId,
273 ) -> Option<ScopeId> {
274 if let Some(expr) = cond.expr() {
275 compute_expr_scopes(expr, scopes, scope);
276 }
277 if let Some(pat) = cond.pat() {
278 let s = scopes.new_scope(scope);
279 scopes.add_bindings(s, pat);
280 Some(s)
281 } else {
282 None
283 }
284 }
285}
286
287#[derive(Debug)]
288pub struct ReferenceDescriptor {
289 pub range: TextRange,
290 pub name: String,
291}
292
293#[cfg(test)]
294mod tests {
295 use ra_editor::find_node_at_offset;
296 use ra_syntax::SourceFileNode;
297 use test_utils::extract_offset;
298
299 use super::*;
300
301 fn do_check(code: &str, expected: &[&str]) {
302 let (off, code) = extract_offset(code);
303 let code = {
304 let mut buf = String::new();
305 let off = u32::from(off) as usize;
306 buf.push_str(&code[..off]);
307 buf.push_str("marker");
308 buf.push_str(&code[off..]);
309 buf
310 };
311 let file = SourceFileNode::parse(&code);
312 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
313 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
314 let scopes = FnScopes::new(fn_def);
315 let actual = scopes
316 .scope_chain(marker.syntax())
317 .flat_map(|scope| scopes.entries(scope))
318 .map(|it| it.name())
319 .collect::<Vec<_>>();
320 assert_eq!(actual.as_slice(), expected);
321 }
322
323 #[test]
324 fn test_lambda_scope() {
325 do_check(
326 r"
327 fn quux(foo: i32) {
328 let f = |bar, baz: i32| {
329 <|>
330 };
331 }",
332 &["bar", "baz", "foo"],
333 );
334 }
335
336 #[test]
337 fn test_call_scope() {
338 do_check(
339 r"
340 fn quux() {
341 f(|x| <|> );
342 }",
343 &["x"],
344 );
345 }
346
347 #[test]
348 fn test_metod_call_scope() {
349 do_check(
350 r"
351 fn quux() {
352 z.f(|x| <|> );
353 }",
354 &["x"],
355 );
356 }
357
358 #[test]
359 fn test_loop_scope() {
360 do_check(
361 r"
362 fn quux() {
363 loop {
364 let x = ();
365 <|>
366 };
367 }",
368 &["x"],
369 );
370 }
371
372 #[test]
373 fn test_match() {
374 do_check(
375 r"
376 fn quux() {
377 match () {
378 Some(x) => {
379 <|>
380 }
381 };
382 }",
383 &["x"],
384 );
385 }
386
387 #[test]
388 fn test_shadow_variable() {
389 do_check(
390 r"
391 fn foo(x: String) {
392 let x : &str = &x<|>;
393 }",
394 &["x"],
395 );
396 }
397
398 fn do_check_local_name(code: &str, expected_offset: u32) {
399 let (off, code) = extract_offset(code);
400 let file = SourceFileNode::parse(&code);
401 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
402 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
403
404 let scopes = FnScopes::new(fn_def);
405
406 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
407 let local_name = local_name_entry.ptr().resolve(&file);
408 let expected_name =
409 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
410 assert_eq!(local_name.range(), expected_name.syntax().range());
411 }
412
413 #[test]
414 fn test_resolve_local_name() {
415 do_check_local_name(
416 r#"
417 fn foo(x: i32, y: u32) {
418 {
419 let z = x * 2;
420 }
421 {
422 let t = x<|> * 3;
423 }
424 }"#,
425 21,
426 );
427 }
428
429 #[test]
430 fn test_resolve_local_name_declaration() {
431 do_check_local_name(
432 r#"
433 fn foo(x: String) {
434 let x : &str = &x<|>;
435 }"#,
436 21,
437 );
438 }
439
440 #[test]
441 fn test_resolve_local_name_shadow() {
442 do_check_local_name(
443 r"
444 fn foo(x: String) {
445 let x : &str = &x;
446 x<|>
447 }",
448 46,
449 );
450 }
451}