aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/code_model_impl/function
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-01-19 18:47:56 +0000
committerFlorian Diebold <[email protected]>2019-01-30 20:23:50 +0000
commit65864d85f992300bad3913438a542af1bd736a24 (patch)
treec2030da2bcd55fc1033a2c0602b7a9349c2dd702 /crates/ra_hir/src/code_model_impl/function
parentc65e6cdcb3d603ce7c0943785f7140662022c54a (diff)
Rename FnScopes -> ExprScopes
The reason for this is that it describes scopes for any body expression, not just that of a function. It did not actually refer to functions at all anymore.
Diffstat (limited to 'crates/ra_hir/src/code_model_impl/function')
-rw-r--r--crates/ra_hir/src/code_model_impl/function/scope.rs510
1 files changed, 0 insertions, 510 deletions
diff --git a/crates/ra_hir/src/code_model_impl/function/scope.rs b/crates/ra_hir/src/code_model_impl/function/scope.rs
deleted file mode 100644
index c5d1de5eb..000000000
--- a/crates/ra_hir/src/code_model_impl/function/scope.rs
+++ /dev/null
@@ -1,510 +0,0 @@
1use std::sync::Arc;
2
3use rustc_hash::{FxHashMap, FxHashSet};
4
5use ra_syntax::{
6 AstNode, SyntaxNode, TextUnit, TextRange, SyntaxNodePtr,
7 algo::generate,
8 ast,
9};
10use ra_arena::{Arena, RawId, impl_arena_id};
11
12use crate::{Name, AsName, expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySyntaxMapping}};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct ScopeId(RawId);
16impl_arena_id!(ScopeId);
17
18#[derive(Debug, PartialEq, Eq)]
19pub struct FnScopes {
20 body: Arc<Body>,
21 scopes: Arena<ScopeId, ScopeData>,
22 scope_for: FxHashMap<ExprId, ScopeId>,
23}
24
25#[derive(Debug, PartialEq, Eq)]
26pub struct ScopeEntry {
27 name: Name,
28 pat: PatId,
29}
30
31#[derive(Debug, PartialEq, Eq)]
32pub struct ScopeData {
33 parent: Option<ScopeId>,
34 entries: Vec<ScopeEntry>,
35}
36
37impl FnScopes {
38 pub(crate) fn new(body: Arc<Body>) -> FnScopes {
39 let mut scopes = FnScopes {
40 body: body.clone(),
41 scopes: Arena::default(),
42 scope_for: FxHashMap::default(),
43 };
44 let root = scopes.root_scope();
45 scopes.add_params_bindings(root, body.params());
46 compute_expr_scopes(body.body_expr(), &body, &mut scopes, root);
47 scopes
48 }
49
50 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
51 &self.scopes[scope].entries
52 }
53
54 pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator<Item = ScopeId> + 'a {
55 generate(self.scope_for(expr), move |&scope| {
56 self.scopes[scope].parent
57 })
58 }
59
60 pub fn resolve_local_name<'a>(
61 &'a self,
62 context_expr: ExprId,
63 name: Name,
64 ) -> Option<&'a ScopeEntry> {
65 let mut shadowed = FxHashSet::default();
66 let ret = self
67 .scope_chain_for(context_expr)
68 .flat_map(|scope| self.entries(scope).iter())
69 .filter(|entry| shadowed.insert(entry.name()))
70 .find(|entry| entry.name() == &name);
71 ret
72 }
73
74 fn root_scope(&mut self) -> ScopeId {
75 self.scopes.alloc(ScopeData {
76 parent: None,
77 entries: vec![],
78 })
79 }
80
81 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
82 self.scopes.alloc(ScopeData {
83 parent: Some(parent),
84 entries: vec![],
85 })
86 }
87
88 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
89 match &body[pat] {
90 Pat::Bind { name, .. } => {
91 // bind can have a subpattern, but it's actually not allowed
92 // to bind to things in there
93 let entry = ScopeEntry {
94 name: name.clone(),
95 pat,
96 };
97 self.scopes[scope].entries.push(entry)
98 }
99 p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)),
100 }
101 }
102
103 fn add_params_bindings(&mut self, scope: ScopeId, params: &[PatId]) {
104 let body = Arc::clone(&self.body);
105 params
106 .into_iter()
107 .for_each(|pat| self.add_bindings(&body, scope, *pat));
108 }
109
110 fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
111 self.scope_for.insert(node, scope);
112 }
113
114 fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
115 self.scope_for.get(&expr).map(|&scope| scope)
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct ScopesWithSyntaxMapping {
121 pub syntax_mapping: Arc<BodySyntaxMapping>,
122 pub scopes: Arc<FnScopes>,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct ScopeEntryWithSyntax {
127 name: Name,
128 ptr: SyntaxNodePtr,
129}
130
131impl ScopeEntryWithSyntax {
132 pub fn name(&self) -> &Name {
133 &self.name
134 }
135
136 pub fn ptr(&self) -> SyntaxNodePtr {
137 self.ptr
138 }
139}
140
141impl ScopesWithSyntaxMapping {
142 pub fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator<Item = ScopeId> + 'a {
143 generate(self.scope_for(node), move |&scope| {
144 self.scopes.scopes[scope].parent
145 })
146 }
147
148 pub fn scope_chain_for_offset<'a>(
149 &'a self,
150 offset: TextUnit,
151 ) -> impl Iterator<Item = ScopeId> + 'a {
152 let scope = self
153 .scopes
154 .scope_for
155 .iter()
156 .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
157 // find containing scope
158 .min_by_key(|(ptr, _scope)| {
159 (
160 !(ptr.range().start() <= offset && offset <= ptr.range().end()),
161 ptr.range().len(),
162 )
163 })
164 .map(|(ptr, scope)| self.adjust(ptr, *scope, offset));
165
166 generate(scope, move |&scope| self.scopes.scopes[scope].parent)
167 }
168
169 // XXX: during completion, cursor might be outside of any particular
170 // expression. Try to figure out the correct scope...
171 fn adjust(&self, ptr: SyntaxNodePtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
172 let r = ptr.range();
173 let child_scopes = self
174 .scopes
175 .scope_for
176 .iter()
177 .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
178 .map(|(ptr, scope)| (ptr.range(), scope))
179 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
180
181 child_scopes
182 .max_by(|(r1, _), (r2, _)| {
183 if r2.is_subrange(&r1) {
184 std::cmp::Ordering::Greater
185 } else if r1.is_subrange(&r2) {
186 std::cmp::Ordering::Less
187 } else {
188 r1.start().cmp(&r2.start())
189 }
190 })
191 .map(|(_ptr, scope)| *scope)
192 .unwrap_or(original_scope)
193 }
194
195 pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> {
196 let mut shadowed = FxHashSet::default();
197 let name = name_ref.as_name();
198 let ret = self
199 .scope_chain(name_ref.syntax())
200 .flat_map(|scope| self.scopes.entries(scope).iter())
201 .filter(|entry| shadowed.insert(entry.name()))
202 .filter(|entry| entry.name() == &name)
203 .nth(0);
204 ret.and_then(|entry| {
205 Some(ScopeEntryWithSyntax {
206 name: entry.name().clone(),
207 ptr: self.syntax_mapping.pat_syntax(entry.pat())?,
208 })
209 })
210 }
211
212 pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
213 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
214 let name_ptr = SyntaxNodePtr::new(pat.syntax());
215 fn_def
216 .syntax()
217 .descendants()
218 .filter_map(ast::NameRef::cast)
219 .filter(|name_ref| match self.resolve_local_name(*name_ref) {
220 None => false,
221 Some(entry) => entry.ptr() == name_ptr,
222 })
223 .map(|name_ref| ReferenceDescriptor {
224 name: name_ref.syntax().text().to_string(),
225 range: name_ref.syntax().range(),
226 })
227 .collect()
228 }
229
230 fn scope_for(&self, node: &SyntaxNode) -> Option<ScopeId> {
231 node.ancestors()
232 .map(SyntaxNodePtr::new)
233 .filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr))
234 .find_map(|it| self.scopes.scope_for(it))
235 }
236}
237
238impl ScopeEntry {
239 pub fn name(&self) -> &Name {
240 &self.name
241 }
242
243 pub fn pat(&self) -> PatId {
244 self.pat
245 }
246}
247
248fn compute_block_scopes(
249 statements: &[Statement],
250 tail: Option<ExprId>,
251 body: &Body,
252 scopes: &mut FnScopes,
253 mut scope: ScopeId,
254) {
255 for stmt in statements {
256 match stmt {
257 Statement::Let {
258 pat, initializer, ..
259 } => {
260 if let Some(expr) = initializer {
261 scopes.set_scope(*expr, scope);
262 compute_expr_scopes(*expr, body, scopes, scope);
263 }
264 scope = scopes.new_scope(scope);
265 scopes.add_bindings(body, scope, *pat);
266 }
267 Statement::Expr(expr) => {
268 scopes.set_scope(*expr, scope);
269 compute_expr_scopes(*expr, body, scopes, scope);
270 }
271 }
272 }
273 if let Some(expr) = tail {
274 compute_expr_scopes(expr, body, scopes, scope);
275 }
276}
277
278fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut FnScopes, scope: ScopeId) {
279 scopes.set_scope(expr, scope);
280 match &body[expr] {
281 Expr::Block { statements, tail } => {
282 compute_block_scopes(&statements, *tail, body, scopes, scope);
283 }
284 Expr::For {
285 iterable,
286 pat,
287 body: body_expr,
288 } => {
289 compute_expr_scopes(*iterable, body, scopes, scope);
290 let scope = scopes.new_scope(scope);
291 scopes.add_bindings(body, scope, *pat);
292 compute_expr_scopes(*body_expr, body, scopes, scope);
293 }
294 Expr::Lambda {
295 args,
296 body: body_expr,
297 ..
298 } => {
299 let scope = scopes.new_scope(scope);
300 scopes.add_params_bindings(scope, &args);
301 compute_expr_scopes(*body_expr, body, scopes, scope);
302 }
303 Expr::Match { expr, arms } => {
304 compute_expr_scopes(*expr, body, scopes, scope);
305 for arm in arms {
306 let scope = scopes.new_scope(scope);
307 for pat in &arm.pats {
308 scopes.add_bindings(body, scope, *pat);
309 }
310 scopes.set_scope(arm.expr, scope);
311 compute_expr_scopes(arm.expr, body, scopes, scope);
312 }
313 }
314 e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
315 };
316}
317
318#[derive(Debug)]
319pub struct ReferenceDescriptor {
320 pub range: TextRange,
321 pub name: String,
322}
323
324#[cfg(test)]
325mod tests {
326 use ra_syntax::{SourceFile, algo::find_node_at_offset};
327 use test_utils::{extract_offset, assert_eq_text};
328
329 use crate::expr;
330
331 use super::*;
332
333 fn do_check(code: &str, expected: &[&str]) {
334 let (off, code) = extract_offset(code);
335 let code = {
336 let mut buf = String::new();
337 let off = u32::from(off) as usize;
338 buf.push_str(&code[..off]);
339 buf.push_str("marker");
340 buf.push_str(&code[off..]);
341 buf
342 };
343 let file = SourceFile::parse(&code);
344 let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
345 let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
346 let body_hir = expr::collect_fn_body_syntax(fn_def);
347 let scopes = FnScopes::new(Arc::clone(body_hir.body()));
348 let scopes = ScopesWithSyntaxMapping {
349 scopes: Arc::new(scopes),
350 syntax_mapping: Arc::new(body_hir),
351 };
352 let actual = scopes
353 .scope_chain(marker.syntax())
354 .flat_map(|scope| scopes.scopes.entries(scope))
355 .map(|it| it.name().to_string())
356 .collect::<Vec<_>>()
357 .join("\n");
358 let expected = expected.join("\n");
359 assert_eq_text!(&expected, &actual);
360 }
361
362 #[test]
363 fn test_lambda_scope() {
364 do_check(
365 r"
366 fn quux(foo: i32) {
367 let f = |bar, baz: i32| {
368 <|>
369 };
370 }",
371 &["bar", "baz", "foo"],
372 );
373 }
374
375 #[test]
376 fn test_call_scope() {
377 do_check(
378 r"
379 fn quux() {
380 f(|x| <|> );
381 }",
382 &["x"],
383 );
384 }
385
386 #[test]
387 fn test_method_call_scope() {
388 do_check(
389 r"
390 fn quux() {
391 z.f(|x| <|> );
392 }",
393 &["x"],
394 );
395 }
396
397 #[test]
398 fn test_loop_scope() {
399 do_check(
400 r"
401 fn quux() {
402 loop {
403 let x = ();
404 <|>
405 };
406 }",
407 &["x"],
408 );
409 }
410
411 #[test]
412 fn test_match() {
413 do_check(
414 r"
415 fn quux() {
416 match () {
417 Some(x) => {
418 <|>
419 }
420 };
421 }",
422 &["x"],
423 );
424 }
425
426 #[test]
427 fn test_shadow_variable() {
428 do_check(
429 r"
430 fn foo(x: String) {
431 let x : &str = &x<|>;
432 }",
433 &["x"],
434 );
435 }
436
437 fn do_check_local_name(code: &str, expected_offset: u32) {
438 let (off, code) = extract_offset(code);
439 let file = SourceFile::parse(&code);
440 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
441 .expect("failed to find a name at the target offset");
442
443 let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
444 let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
445
446 let body_hir = expr::collect_fn_body_syntax(fn_def);
447 let scopes = FnScopes::new(Arc::clone(body_hir.body()));
448 let scopes = ScopesWithSyntaxMapping {
449 scopes: Arc::new(scopes),
450 syntax_mapping: Arc::new(body_hir),
451 };
452 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
453 let local_name = local_name_entry.ptr();
454 assert_eq!(local_name.range(), expected_name.syntax().range());
455 }
456
457 #[test]
458 fn test_resolve_local_name() {
459 do_check_local_name(
460 r#"
461 fn foo(x: i32, y: u32) {
462 {
463 let z = x * 2;
464 }
465 {
466 let t = x<|> * 3;
467 }
468 }"#,
469 21,
470 );
471 }
472
473 #[test]
474 fn test_resolve_local_name_declaration() {
475 do_check_local_name(
476 r#"
477 fn foo(x: String) {
478 let x : &str = &x<|>;
479 }"#,
480 21,
481 );
482 }
483
484 #[test]
485 fn test_resolve_local_name_shadow() {
486 do_check_local_name(
487 r"
488 fn foo(x: String) {
489 let x : &str = &x;
490 x<|>
491 }
492 ",
493 53,
494 );
495 }
496
497 #[test]
498 fn ref_patterns_contribute_bindings() {
499 do_check_local_name(
500 r"
501 fn foo() {
502 if let Some(&from) = bar() {
503 from<|>;
504 }
505 }
506 ",
507 53,
508 );
509 }
510}