diff options
author | Florian Diebold <[email protected]> | 2019-01-05 21:37:59 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-01-05 23:29:36 +0000 |
commit | 8e3e5ab2c81f238ea4e731f55eac79b74d9d84c3 (patch) | |
tree | e0388878b4d94ae71fbf82d3e3163c49c8e69c16 /crates/ra_hir/src/function | |
parent | 136aba1cf32646278c4034541ee415f656f8bb5e (diff) |
Make FnScopes use hir::Expr
This was a bit complicated. I've added a wrapper type for now that does the
LocalSyntaxPtr <-> ExprId translation; we might want to get rid of that or give
it a nicer interface.
Diffstat (limited to 'crates/ra_hir/src/function')
-rw-r--r-- | crates/ra_hir/src/function/scope.rs | 368 |
1 files changed, 178 insertions, 190 deletions
diff --git a/crates/ra_hir/src/function/scope.rs b/crates/ra_hir/src/function/scope.rs index 42bfe4f32..0607a99cb 100644 --- a/crates/ra_hir/src/function/scope.rs +++ b/crates/ra_hir/src/function/scope.rs | |||
@@ -1,14 +1,16 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
1 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::{FxHashMap, FxHashSet}; |
2 | 4 | ||
3 | use ra_syntax::{ | 5 | use ra_syntax::{ |
4 | AstNode, SyntaxNodeRef, TextUnit, TextRange, | 6 | AstNode, SyntaxNodeRef, TextUnit, TextRange, |
5 | algo::generate, | 7 | algo::generate, |
6 | ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, | 8 | ast, |
7 | }; | 9 | }; |
8 | use ra_arena::{Arena, RawId, impl_arena_id}; | 10 | use ra_arena::{Arena, RawId, impl_arena_id}; |
9 | use ra_db::LocalSyntaxPtr; | 11 | use ra_db::LocalSyntaxPtr; |
10 | 12 | ||
11 | use crate::{Name, AsName}; | 13 | use crate::{Name, AsName, expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySyntaxMapping}}; |
12 | 14 | ||
13 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 15 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
14 | pub struct ScopeId(RawId); | 16 | pub struct ScopeId(RawId); |
@@ -16,15 +18,15 @@ impl_arena_id!(ScopeId); | |||
16 | 18 | ||
17 | #[derive(Debug, PartialEq, Eq)] | 19 | #[derive(Debug, PartialEq, Eq)] |
18 | pub struct FnScopes { | 20 | pub struct FnScopes { |
19 | pub self_param: Option<LocalSyntaxPtr>, | 21 | body: Arc<Body>, |
20 | scopes: Arena<ScopeId, ScopeData>, | 22 | scopes: Arena<ScopeId, ScopeData>, |
21 | scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>, | 23 | scope_for: FxHashMap<ExprId, ScopeId>, |
22 | } | 24 | } |
23 | 25 | ||
24 | #[derive(Debug, PartialEq, Eq)] | 26 | #[derive(Debug, PartialEq, Eq)] |
25 | pub struct ScopeEntry { | 27 | pub struct ScopeEntry { |
26 | name: Name, | 28 | name: Name, |
27 | ptr: LocalSyntaxPtr, | 29 | pat: PatId, |
28 | } | 30 | } |
29 | 31 | ||
30 | #[derive(Debug, PartialEq, Eq)] | 32 | #[derive(Debug, PartialEq, Eq)] |
@@ -34,28 +36,101 @@ pub struct ScopeData { | |||
34 | } | 36 | } |
35 | 37 | ||
36 | impl FnScopes { | 38 | impl FnScopes { |
37 | pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes { | 39 | pub(crate) fn new(body: Arc<Body>) -> FnScopes { |
38 | let mut scopes = FnScopes { | 40 | let mut scopes = FnScopes { |
39 | self_param: fn_def | 41 | body: body.clone(), |
40 | .param_list() | ||
41 | .and_then(|it| it.self_param()) | ||
42 | .map(|it| LocalSyntaxPtr::new(it.syntax())), | ||
43 | scopes: Arena::default(), | 42 | scopes: Arena::default(), |
44 | scope_for: FxHashMap::default(), | 43 | scope_for: FxHashMap::default(), |
45 | }; | 44 | }; |
46 | let root = scopes.root_scope(); | 45 | let root = scopes.root_scope(); |
47 | scopes.add_params_bindings(root, fn_def.param_list()); | 46 | scopes.add_params_bindings(root, body.args()); |
48 | if let Some(body) = fn_def.body() { | 47 | compute_expr_scopes(body.body_expr(), &body, &mut scopes, root); |
49 | compute_block_scopes(body, &mut scopes, root) | ||
50 | } | ||
51 | scopes | 48 | scopes |
52 | } | 49 | } |
53 | pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { | 50 | pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { |
54 | &self.scopes[scope].entries | 51 | &self.scopes[scope].entries |
55 | } | 52 | } |
53 | pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator<Item = ScopeId> + 'a { | ||
54 | generate(self.scope_for(expr), move |&scope| { | ||
55 | self.scopes[scope].parent | ||
56 | }) | ||
57 | } | ||
58 | |||
59 | pub fn resolve_local_name<'a>( | ||
60 | &'a self, | ||
61 | context_expr: ExprId, | ||
62 | name: Name, | ||
63 | ) -> Option<&'a ScopeEntry> { | ||
64 | let mut shadowed = FxHashSet::default(); | ||
65 | let ret = self | ||
66 | .scope_chain_for(context_expr) | ||
67 | .flat_map(|scope| self.entries(scope).iter()) | ||
68 | .filter(|entry| shadowed.insert(entry.name())) | ||
69 | .filter(|entry| entry.name() == &name) | ||
70 | .nth(0); | ||
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 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | ||
81 | self.scopes.alloc(ScopeData { | ||
82 | parent: Some(parent), | ||
83 | entries: vec![], | ||
84 | }) | ||
85 | } | ||
86 | fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { | ||
87 | match body.pat(pat) { | ||
88 | Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry { | ||
89 | name: name.clone(), | ||
90 | pat, | ||
91 | }), | ||
92 | p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)), | ||
93 | } | ||
94 | } | ||
95 | fn add_params_bindings(&mut self, scope: ScopeId, params: &[PatId]) { | ||
96 | let body = Arc::clone(&self.body); | ||
97 | params | ||
98 | .into_iter() | ||
99 | .for_each(|it| self.add_bindings(&body, scope, *it)); | ||
100 | } | ||
101 | fn set_scope(&mut self, node: ExprId, scope: ScopeId) { | ||
102 | self.scope_for.insert(node, scope); | ||
103 | } | ||
104 | fn scope_for(&self, expr: ExprId) -> Option<ScopeId> { | ||
105 | self.scope_for.get(&expr).map(|&scope| scope) | ||
106 | } | ||
107 | } | ||
108 | |||
109 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
110 | pub struct ScopesWithSyntaxMapping { | ||
111 | pub syntax_mapping: Arc<BodySyntaxMapping>, | ||
112 | pub scopes: Arc<FnScopes>, | ||
113 | } | ||
114 | |||
115 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
116 | pub struct ScopeEntryWithSyntax { | ||
117 | name: Name, | ||
118 | ptr: LocalSyntaxPtr, | ||
119 | } | ||
120 | |||
121 | impl ScopeEntryWithSyntax { | ||
122 | pub fn name(&self) -> &Name { | ||
123 | &self.name | ||
124 | } | ||
125 | pub fn ptr(&self) -> LocalSyntaxPtr { | ||
126 | self.ptr | ||
127 | } | ||
128 | } | ||
129 | |||
130 | impl ScopesWithSyntaxMapping { | ||
56 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { | 131 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { |
57 | generate(self.scope_for(node), move |&scope| { | 132 | generate(self.scope_for(node), move |&scope| { |
58 | self.scopes[scope].parent | 133 | self.scopes.scopes[scope].parent |
59 | }) | 134 | }) |
60 | } | 135 | } |
61 | pub fn scope_chain_for_offset<'a>( | 136 | pub fn scope_chain_for_offset<'a>( |
@@ -63,26 +138,30 @@ impl FnScopes { | |||
63 | offset: TextUnit, | 138 | offset: TextUnit, |
64 | ) -> impl Iterator<Item = ScopeId> + 'a { | 139 | ) -> impl Iterator<Item = ScopeId> + 'a { |
65 | let scope = self | 140 | let scope = self |
141 | .scopes | ||
66 | .scope_for | 142 | .scope_for |
67 | .iter() | 143 | .iter() |
68 | // find containin scope | 144 | .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope))) |
145 | // find containing scope | ||
69 | .min_by_key(|(ptr, _scope)| { | 146 | .min_by_key(|(ptr, _scope)| { |
70 | ( | 147 | ( |
71 | !(ptr.range().start() <= offset && offset <= ptr.range().end()), | 148 | !(ptr.range().start() <= offset && offset <= ptr.range().end()), |
72 | ptr.range().len(), | 149 | ptr.range().len(), |
73 | ) | 150 | ) |
74 | }) | 151 | }) |
75 | .map(|(ptr, scope)| self.adjust(*ptr, *scope, offset)); | 152 | .map(|(ptr, scope)| self.adjust(ptr, *scope, offset)); |
76 | 153 | ||
77 | generate(scope, move |&scope| self.scopes[scope].parent) | 154 | generate(scope, move |&scope| self.scopes.scopes[scope].parent) |
78 | } | 155 | } |
79 | // XXX: during completion, cursor might be outside of any particular | 156 | // XXX: during completion, cursor might be outside of any particular |
80 | // expression. Try to figure out the correct scope... | 157 | // expression. Try to figure out the correct scope... |
81 | fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { | 158 | fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { |
82 | let r = ptr.range(); | 159 | let r = ptr.range(); |
83 | let child_scopes = self | 160 | let child_scopes = self |
161 | .scopes | ||
84 | .scope_for | 162 | .scope_for |
85 | .iter() | 163 | .iter() |
164 | .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope))) | ||
86 | .map(|(ptr, scope)| (ptr.range(), scope)) | 165 | .map(|(ptr, scope)| (ptr.range(), scope)) |
87 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); | 166 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); |
88 | 167 | ||
@@ -100,22 +179,27 @@ impl FnScopes { | |||
100 | .unwrap_or(original_scope) | 179 | .unwrap_or(original_scope) |
101 | } | 180 | } |
102 | 181 | ||
103 | pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> { | 182 | pub fn resolve_local_name(&self, name_ref: ast::NameRef) -> Option<ScopeEntryWithSyntax> { |
104 | let mut shadowed = FxHashSet::default(); | 183 | let mut shadowed = FxHashSet::default(); |
105 | let name = name_ref.as_name(); | 184 | let name = name_ref.as_name(); |
106 | let ret = self | 185 | let ret = self |
107 | .scope_chain(name_ref.syntax()) | 186 | .scope_chain(name_ref.syntax()) |
108 | .flat_map(|scope| self.entries(scope).iter()) | 187 | .flat_map(|scope| self.scopes.entries(scope).iter()) |
109 | .filter(|entry| shadowed.insert(entry.name())) | 188 | .filter(|entry| shadowed.insert(entry.name())) |
110 | .filter(|entry| entry.name() == &name) | 189 | .filter(|entry| entry.name() == &name) |
111 | .nth(0); | 190 | .nth(0); |
112 | ret | 191 | ret.and_then(|entry| { |
192 | Some(ScopeEntryWithSyntax { | ||
193 | name: entry.name().clone(), | ||
194 | ptr: self.syntax_mapping.pat_syntax(entry.pat())?, | ||
195 | }) | ||
196 | }) | ||
113 | } | 197 | } |
114 | 198 | ||
115 | pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> { | 199 | pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> { |
116 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | 200 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); |
117 | let name_ptr = LocalSyntaxPtr::new(pat.syntax()); | 201 | let name_ptr = LocalSyntaxPtr::new(pat.syntax()); |
118 | let refs: Vec<_> = fn_def | 202 | fn_def |
119 | .syntax() | 203 | .syntax() |
120 | .descendants() | 204 | .descendants() |
121 | .filter_map(ast::NameRef::cast) | 205 | .filter_map(ast::NameRef::cast) |
@@ -127,203 +211,95 @@ impl FnScopes { | |||
127 | name: name_ref.syntax().text().to_string(), | 211 | name: name_ref.syntax().text().to_string(), |
128 | range: name_ref.syntax().range(), | 212 | range: name_ref.syntax().range(), |
129 | }) | 213 | }) |
130 | .collect(); | 214 | .collect() |
131 | |||
132 | refs | ||
133 | } | 215 | } |
134 | 216 | ||
135 | fn root_scope(&mut self) -> ScopeId { | ||
136 | self.scopes.alloc(ScopeData { | ||
137 | parent: None, | ||
138 | entries: vec![], | ||
139 | }) | ||
140 | } | ||
141 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | ||
142 | self.scopes.alloc(ScopeData { | ||
143 | parent: Some(parent), | ||
144 | entries: vec![], | ||
145 | }) | ||
146 | } | ||
147 | fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) { | ||
148 | let entries = pat | ||
149 | .syntax() | ||
150 | .descendants() | ||
151 | .filter_map(ast::BindPat::cast) | ||
152 | .filter_map(ScopeEntry::new); | ||
153 | self.scopes[scope].entries.extend(entries); | ||
154 | } | ||
155 | fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) { | ||
156 | params | ||
157 | .into_iter() | ||
158 | .flat_map(|it| it.params()) | ||
159 | .filter_map(|it| it.pat()) | ||
160 | .for_each(|it| self.add_bindings(scope, it)); | ||
161 | } | ||
162 | fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) { | ||
163 | self.scope_for.insert(LocalSyntaxPtr::new(node), scope); | ||
164 | } | ||
165 | fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> { | 217 | fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> { |
166 | node.ancestors() | 218 | node.ancestors() |
167 | .map(LocalSyntaxPtr::new) | 219 | .map(LocalSyntaxPtr::new) |
168 | .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope)) | 220 | .filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr)) |
221 | .filter_map(|it| self.scopes.scope_for(it)) | ||
169 | .next() | 222 | .next() |
170 | } | 223 | } |
171 | } | 224 | } |
172 | 225 | ||
173 | impl ScopeEntry { | 226 | impl ScopeEntry { |
174 | fn new(pat: ast::BindPat) -> Option<ScopeEntry> { | ||
175 | let name = pat.name()?.as_name(); | ||
176 | let res = ScopeEntry { | ||
177 | name, | ||
178 | ptr: LocalSyntaxPtr::new(pat.syntax()), | ||
179 | }; | ||
180 | Some(res) | ||
181 | } | ||
182 | pub fn name(&self) -> &Name { | 227 | pub fn name(&self) -> &Name { |
183 | &self.name | 228 | &self.name |
184 | } | 229 | } |
185 | pub fn ptr(&self) -> LocalSyntaxPtr { | 230 | pub fn pat(&self) -> PatId { |
186 | self.ptr | 231 | self.pat |
187 | } | 232 | } |
188 | } | 233 | } |
189 | 234 | ||
190 | fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { | 235 | fn compute_block_scopes( |
191 | // A hack for completion :( | 236 | statements: &[Statement], |
192 | scopes.set_scope(block.syntax(), scope); | 237 | tail: Option<ExprId>, |
193 | for stmt in block.statements() { | 238 | body: &Body, |
239 | scopes: &mut FnScopes, | ||
240 | mut scope: ScopeId, | ||
241 | ) { | ||
242 | for stmt in statements { | ||
194 | match stmt { | 243 | match stmt { |
195 | ast::Stmt::LetStmt(stmt) => { | 244 | Statement::Let { |
196 | if let Some(expr) = stmt.initializer() { | 245 | pat, initializer, .. |
197 | scopes.set_scope(expr.syntax(), scope); | 246 | } => { |
198 | compute_expr_scopes(expr, scopes, scope); | 247 | if let Some(expr) = initializer { |
248 | scopes.set_scope(*expr, scope); | ||
249 | compute_expr_scopes(*expr, body, scopes, scope); | ||
199 | } | 250 | } |
200 | scope = scopes.new_scope(scope); | 251 | scope = scopes.new_scope(scope); |
201 | if let Some(pat) = stmt.pat() { | 252 | scopes.add_bindings(body, scope, *pat); |
202 | scopes.add_bindings(scope, pat); | ||
203 | } | ||
204 | } | 253 | } |
205 | ast::Stmt::ExprStmt(expr_stmt) => { | 254 | Statement::Expr(expr) => { |
206 | if let Some(expr) = expr_stmt.expr() { | 255 | scopes.set_scope(*expr, scope); |
207 | scopes.set_scope(expr.syntax(), scope); | 256 | compute_expr_scopes(*expr, body, scopes, scope); |
208 | compute_expr_scopes(expr, scopes, scope); | ||
209 | } | ||
210 | } | 257 | } |
211 | } | 258 | } |
212 | } | 259 | } |
213 | if let Some(expr) = block.expr() { | 260 | if let Some(expr) = tail { |
214 | scopes.set_scope(expr.syntax(), scope); | 261 | compute_expr_scopes(expr, body, scopes, scope); |
215 | compute_expr_scopes(expr, scopes, scope); | ||
216 | } | 262 | } |
217 | } | 263 | } |
218 | 264 | ||
219 | fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | 265 | fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut FnScopes, scope: ScopeId) { |
220 | match expr { | 266 | scopes.set_scope(expr, scope); |
221 | ast::Expr::IfExpr(e) => { | 267 | match body.expr(expr) { |
222 | let cond_scope = e | 268 | Expr::Block { statements, tail } => { |
223 | .condition() | 269 | compute_block_scopes(&statements, *tail, body, scopes, scope); |
224 | .and_then(|cond| compute_cond_scopes(cond, scopes, scope)); | ||
225 | if let Some(block) = e.then_branch() { | ||
226 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | ||
227 | } | ||
228 | if let Some(block) = e.else_branch() { | ||
229 | compute_block_scopes(block, scopes, scope); | ||
230 | } | ||
231 | } | ||
232 | ast::Expr::BlockExpr(e) => { | ||
233 | if let Some(block) = e.block() { | ||
234 | compute_block_scopes(block, scopes, scope); | ||
235 | } | ||
236 | } | ||
237 | ast::Expr::LoopExpr(e) => { | ||
238 | if let Some(block) = e.loop_body() { | ||
239 | compute_block_scopes(block, scopes, scope); | ||
240 | } | ||
241 | } | ||
242 | ast::Expr::WhileExpr(e) => { | ||
243 | let cond_scope = e | ||
244 | .condition() | ||
245 | .and_then(|cond| compute_cond_scopes(cond, scopes, scope)); | ||
246 | if let Some(block) = e.loop_body() { | ||
247 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | ||
248 | } | ||
249 | } | 270 | } |
250 | ast::Expr::ForExpr(e) => { | 271 | Expr::For { |
251 | if let Some(expr) = e.iterable() { | 272 | iterable, |
252 | compute_expr_scopes(expr, scopes, scope); | 273 | pat, |
253 | } | 274 | body: body_expr, |
254 | let mut scope = scope; | 275 | } => { |
255 | if let Some(pat) = e.pat() { | 276 | compute_expr_scopes(*iterable, body, scopes, scope); |
256 | scope = scopes.new_scope(scope); | ||
257 | scopes.add_bindings(scope, pat); | ||
258 | } | ||
259 | if let Some(block) = e.loop_body() { | ||
260 | compute_block_scopes(block, scopes, scope); | ||
261 | } | ||
262 | } | ||
263 | ast::Expr::LambdaExpr(e) => { | ||
264 | let scope = scopes.new_scope(scope); | 277 | let scope = scopes.new_scope(scope); |
265 | scopes.add_params_bindings(scope, e.param_list()); | 278 | scopes.add_bindings(body, scope, *pat); |
266 | if let Some(body) = e.body() { | 279 | compute_expr_scopes(*body_expr, body, scopes, scope); |
267 | scopes.set_scope(body.syntax(), scope); | ||
268 | compute_expr_scopes(body, scopes, scope); | ||
269 | } | ||
270 | } | 280 | } |
271 | ast::Expr::CallExpr(e) => { | 281 | Expr::Lambda { |
272 | compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); | 282 | args, |
273 | } | 283 | body: body_expr, |
274 | ast::Expr::MethodCallExpr(e) => { | 284 | .. |
275 | compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); | 285 | } => { |
286 | let scope = scopes.new_scope(scope); | ||
287 | scopes.add_params_bindings(scope, &args); | ||
288 | compute_expr_scopes(*body_expr, body, scopes, scope); | ||
276 | } | 289 | } |
277 | ast::Expr::MatchExpr(e) => { | 290 | Expr::Match { expr, arms } => { |
278 | if let Some(expr) = e.expr() { | 291 | compute_expr_scopes(*expr, body, scopes, scope); |
279 | compute_expr_scopes(expr, scopes, scope); | 292 | for arm in arms { |
280 | } | ||
281 | for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) { | ||
282 | let scope = scopes.new_scope(scope); | 293 | let scope = scopes.new_scope(scope); |
283 | for pat in arm.pats() { | 294 | for pat in &arm.pats { |
284 | scopes.add_bindings(scope, pat); | 295 | scopes.add_bindings(body, scope, *pat); |
285 | } | ||
286 | if let Some(expr) = arm.expr() { | ||
287 | compute_expr_scopes(expr, scopes, scope); | ||
288 | } | 296 | } |
297 | scopes.set_scope(arm.expr, scope); | ||
298 | compute_expr_scopes(arm.expr, body, scopes, scope); | ||
289 | } | 299 | } |
290 | } | 300 | } |
291 | _ => expr | 301 | e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)), |
292 | .syntax() | ||
293 | .children() | ||
294 | .filter_map(ast::Expr::cast) | ||
295 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)), | ||
296 | }; | 302 | }; |
297 | |||
298 | fn compute_call_scopes( | ||
299 | receiver: Option<ast::Expr>, | ||
300 | arg_list: Option<ast::ArgList>, | ||
301 | scopes: &mut FnScopes, | ||
302 | scope: ScopeId, | ||
303 | ) { | ||
304 | arg_list | ||
305 | .into_iter() | ||
306 | .flat_map(|it| it.args()) | ||
307 | .chain(receiver) | ||
308 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); | ||
309 | } | ||
310 | |||
311 | fn compute_cond_scopes( | ||
312 | cond: ast::Condition, | ||
313 | scopes: &mut FnScopes, | ||
314 | scope: ScopeId, | ||
315 | ) -> Option<ScopeId> { | ||
316 | if let Some(expr) = cond.expr() { | ||
317 | compute_expr_scopes(expr, scopes, scope); | ||
318 | } | ||
319 | if let Some(pat) = cond.pat() { | ||
320 | let s = scopes.new_scope(scope); | ||
321 | scopes.add_bindings(s, pat); | ||
322 | Some(s) | ||
323 | } else { | ||
324 | None | ||
325 | } | ||
326 | } | ||
327 | } | 303 | } |
328 | 304 | ||
329 | #[derive(Debug)] | 305 | #[derive(Debug)] |
@@ -338,6 +314,8 @@ mod tests { | |||
338 | use ra_syntax::SourceFileNode; | 314 | use ra_syntax::SourceFileNode; |
339 | use test_utils::{extract_offset, assert_eq_text}; | 315 | use test_utils::{extract_offset, assert_eq_text}; |
340 | 316 | ||
317 | use crate::expr; | ||
318 | |||
341 | use super::*; | 319 | use super::*; |
342 | 320 | ||
343 | fn do_check(code: &str, expected: &[&str]) { | 321 | fn do_check(code: &str, expected: &[&str]) { |
@@ -353,15 +331,20 @@ mod tests { | |||
353 | let file = SourceFileNode::parse(&code); | 331 | let file = SourceFileNode::parse(&code); |
354 | let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); | 332 | let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); |
355 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); | 333 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); |
356 | let scopes = FnScopes::new(fn_def); | 334 | let body_hir = expr::collect_fn_body_syntax(fn_def); |
335 | let scopes = FnScopes::new(Arc::clone(body_hir.body())); | ||
336 | let scopes = ScopesWithSyntaxMapping { | ||
337 | scopes: Arc::new(scopes), | ||
338 | syntax_mapping: Arc::new(body_hir), | ||
339 | }; | ||
357 | let actual = scopes | 340 | let actual = scopes |
358 | .scope_chain(marker.syntax()) | 341 | .scope_chain(marker.syntax()) |
359 | .flat_map(|scope| scopes.entries(scope)) | 342 | .flat_map(|scope| scopes.scopes.entries(scope)) |
360 | .map(|it| it.name().to_string()) | 343 | .map(|it| it.name().to_string()) |
361 | .collect::<Vec<_>>() | 344 | .collect::<Vec<_>>() |
362 | .join("\n"); | 345 | .join("\n"); |
363 | let expected = expected.join("\n"); | 346 | let expected = expected.join("\n"); |
364 | assert_eq_text!(&actual, &expected); | 347 | assert_eq_text!(&expected, &actual); |
365 | } | 348 | } |
366 | 349 | ||
367 | #[test] | 350 | #[test] |
@@ -389,7 +372,7 @@ mod tests { | |||
389 | } | 372 | } |
390 | 373 | ||
391 | #[test] | 374 | #[test] |
392 | fn test_metod_call_scope() { | 375 | fn test_method_call_scope() { |
393 | do_check( | 376 | do_check( |
394 | r" | 377 | r" |
395 | fn quux() { | 378 | fn quux() { |
@@ -445,10 +428,15 @@ mod tests { | |||
445 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); | 428 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); |
446 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); | 429 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); |
447 | 430 | ||
448 | let scopes = FnScopes::new(fn_def); | 431 | let body_hir = expr::collect_fn_body_syntax(fn_def); |
432 | let scopes = FnScopes::new(Arc::clone(body_hir.body())); | ||
433 | let scopes = ScopesWithSyntaxMapping { | ||
434 | scopes: Arc::new(scopes), | ||
435 | syntax_mapping: Arc::new(body_hir), | ||
436 | }; | ||
449 | 437 | ||
450 | let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); | 438 | let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); |
451 | let local_name = local_name_entry.ptr().resolve(&file); | 439 | let local_name = local_name_entry.ptr(); |
452 | let expected_name = | 440 | let expected_name = |
453 | find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | 441 | find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); |
454 | assert_eq!(local_name.range(), expected_name.syntax().range()); | 442 | assert_eq!(local_name.range(), expected_name.syntax().range()); |