diff options
Diffstat (limited to 'crates/ra_hir/src/function/scope.rs')
-rw-r--r-- | crates/ra_hir/src/function/scope.rs | 368 |
1 files changed, 177 insertions, 191 deletions
diff --git a/crates/ra_hir/src/function/scope.rs b/crates/ra_hir/src/function/scope.rs index 42bfe4f32..0a12f0b35 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,100 @@ 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 | .find(|entry| entry.name() == &name); | ||
70 | ret | ||
71 | } | ||
72 | |||
73 | fn root_scope(&mut self) -> ScopeId { | ||
74 | self.scopes.alloc(ScopeData { | ||
75 | parent: None, | ||
76 | entries: vec![], | ||
77 | }) | ||
78 | } | ||
79 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | ||
80 | self.scopes.alloc(ScopeData { | ||
81 | parent: Some(parent), | ||
82 | entries: vec![], | ||
83 | }) | ||
84 | } | ||
85 | fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { | ||
86 | match &body[pat] { | ||
87 | Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry { | ||
88 | name: name.clone(), | ||
89 | pat, | ||
90 | }), | ||
91 | p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)), | ||
92 | } | ||
93 | } | ||
94 | fn add_params_bindings(&mut self, scope: ScopeId, params: &[PatId]) { | ||
95 | let body = Arc::clone(&self.body); | ||
96 | params | ||
97 | .into_iter() | ||
98 | .for_each(|pat| self.add_bindings(&body, scope, *pat)); | ||
99 | } | ||
100 | fn set_scope(&mut self, node: ExprId, scope: ScopeId) { | ||
101 | self.scope_for.insert(node, scope); | ||
102 | } | ||
103 | fn scope_for(&self, expr: ExprId) -> Option<ScopeId> { | ||
104 | self.scope_for.get(&expr).map(|&scope| scope) | ||
105 | } | ||
106 | } | ||
107 | |||
108 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
109 | pub struct ScopesWithSyntaxMapping { | ||
110 | pub syntax_mapping: Arc<BodySyntaxMapping>, | ||
111 | pub scopes: Arc<FnScopes>, | ||
112 | } | ||
113 | |||
114 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
115 | pub struct ScopeEntryWithSyntax { | ||
116 | name: Name, | ||
117 | ptr: LocalSyntaxPtr, | ||
118 | } | ||
119 | |||
120 | impl ScopeEntryWithSyntax { | ||
121 | pub fn name(&self) -> &Name { | ||
122 | &self.name | ||
123 | } | ||
124 | pub fn ptr(&self) -> LocalSyntaxPtr { | ||
125 | self.ptr | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl ScopesWithSyntaxMapping { | ||
56 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { | 130 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { |
57 | generate(self.scope_for(node), move |&scope| { | 131 | generate(self.scope_for(node), move |&scope| { |
58 | self.scopes[scope].parent | 132 | self.scopes.scopes[scope].parent |
59 | }) | 133 | }) |
60 | } | 134 | } |
61 | pub fn scope_chain_for_offset<'a>( | 135 | pub fn scope_chain_for_offset<'a>( |
@@ -63,26 +137,30 @@ impl FnScopes { | |||
63 | offset: TextUnit, | 137 | offset: TextUnit, |
64 | ) -> impl Iterator<Item = ScopeId> + 'a { | 138 | ) -> impl Iterator<Item = ScopeId> + 'a { |
65 | let scope = self | 139 | let scope = self |
140 | .scopes | ||
66 | .scope_for | 141 | .scope_for |
67 | .iter() | 142 | .iter() |
68 | // find containin scope | 143 | .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope))) |
144 | // find containing scope | ||
69 | .min_by_key(|(ptr, _scope)| { | 145 | .min_by_key(|(ptr, _scope)| { |
70 | ( | 146 | ( |
71 | !(ptr.range().start() <= offset && offset <= ptr.range().end()), | 147 | !(ptr.range().start() <= offset && offset <= ptr.range().end()), |
72 | ptr.range().len(), | 148 | ptr.range().len(), |
73 | ) | 149 | ) |
74 | }) | 150 | }) |
75 | .map(|(ptr, scope)| self.adjust(*ptr, *scope, offset)); | 151 | .map(|(ptr, scope)| self.adjust(ptr, *scope, offset)); |
76 | 152 | ||
77 | generate(scope, move |&scope| self.scopes[scope].parent) | 153 | generate(scope, move |&scope| self.scopes.scopes[scope].parent) |
78 | } | 154 | } |
79 | // XXX: during completion, cursor might be outside of any particular | 155 | // XXX: during completion, cursor might be outside of any particular |
80 | // expression. Try to figure out the correct scope... | 156 | // expression. Try to figure out the correct scope... |
81 | fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { | 157 | fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { |
82 | let r = ptr.range(); | 158 | let r = ptr.range(); |
83 | let child_scopes = self | 159 | let child_scopes = self |
160 | .scopes | ||
84 | .scope_for | 161 | .scope_for |
85 | .iter() | 162 | .iter() |
163 | .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope))) | ||
86 | .map(|(ptr, scope)| (ptr.range(), scope)) | 164 | .map(|(ptr, scope)| (ptr.range(), scope)) |
87 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); | 165 | .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); |
88 | 166 | ||
@@ -100,22 +178,27 @@ impl FnScopes { | |||
100 | .unwrap_or(original_scope) | 178 | .unwrap_or(original_scope) |
101 | } | 179 | } |
102 | 180 | ||
103 | pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> { | 181 | pub fn resolve_local_name(&self, name_ref: ast::NameRef) -> Option<ScopeEntryWithSyntax> { |
104 | let mut shadowed = FxHashSet::default(); | 182 | let mut shadowed = FxHashSet::default(); |
105 | let name = name_ref.as_name(); | 183 | let name = name_ref.as_name(); |
106 | let ret = self | 184 | let ret = self |
107 | .scope_chain(name_ref.syntax()) | 185 | .scope_chain(name_ref.syntax()) |
108 | .flat_map(|scope| self.entries(scope).iter()) | 186 | .flat_map(|scope| self.scopes.entries(scope).iter()) |
109 | .filter(|entry| shadowed.insert(entry.name())) | 187 | .filter(|entry| shadowed.insert(entry.name())) |
110 | .filter(|entry| entry.name() == &name) | 188 | .filter(|entry| entry.name() == &name) |
111 | .nth(0); | 189 | .nth(0); |
112 | ret | 190 | ret.and_then(|entry| { |
191 | Some(ScopeEntryWithSyntax { | ||
192 | name: entry.name().clone(), | ||
193 | ptr: self.syntax_mapping.pat_syntax(entry.pat())?, | ||
194 | }) | ||
195 | }) | ||
113 | } | 196 | } |
114 | 197 | ||
115 | pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> { | 198 | pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> { |
116 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | 199 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); |
117 | let name_ptr = LocalSyntaxPtr::new(pat.syntax()); | 200 | let name_ptr = LocalSyntaxPtr::new(pat.syntax()); |
118 | let refs: Vec<_> = fn_def | 201 | fn_def |
119 | .syntax() | 202 | .syntax() |
120 | .descendants() | 203 | .descendants() |
121 | .filter_map(ast::NameRef::cast) | 204 | .filter_map(ast::NameRef::cast) |
@@ -127,203 +210,94 @@ impl FnScopes { | |||
127 | name: name_ref.syntax().text().to_string(), | 210 | name: name_ref.syntax().text().to_string(), |
128 | range: name_ref.syntax().range(), | 211 | range: name_ref.syntax().range(), |
129 | }) | 212 | }) |
130 | .collect(); | 213 | .collect() |
131 | |||
132 | refs | ||
133 | } | 214 | } |
134 | 215 | ||
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> { | 216 | fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> { |
166 | node.ancestors() | 217 | node.ancestors() |
167 | .map(LocalSyntaxPtr::new) | 218 | .map(LocalSyntaxPtr::new) |
168 | .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope)) | 219 | .filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr)) |
169 | .next() | 220 | .find_map(|it| self.scopes.scope_for(it)) |
170 | } | 221 | } |
171 | } | 222 | } |
172 | 223 | ||
173 | impl ScopeEntry { | 224 | 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 { | 225 | pub fn name(&self) -> &Name { |
183 | &self.name | 226 | &self.name |
184 | } | 227 | } |
185 | pub fn ptr(&self) -> LocalSyntaxPtr { | 228 | pub fn pat(&self) -> PatId { |
186 | self.ptr | 229 | self.pat |
187 | } | 230 | } |
188 | } | 231 | } |
189 | 232 | ||
190 | fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { | 233 | fn compute_block_scopes( |
191 | // A hack for completion :( | 234 | statements: &[Statement], |
192 | scopes.set_scope(block.syntax(), scope); | 235 | tail: Option<ExprId>, |
193 | for stmt in block.statements() { | 236 | body: &Body, |
237 | scopes: &mut FnScopes, | ||
238 | mut scope: ScopeId, | ||
239 | ) { | ||
240 | for stmt in statements { | ||
194 | match stmt { | 241 | match stmt { |
195 | ast::Stmt::LetStmt(stmt) => { | 242 | Statement::Let { |
196 | if let Some(expr) = stmt.initializer() { | 243 | pat, initializer, .. |
197 | scopes.set_scope(expr.syntax(), scope); | 244 | } => { |
198 | compute_expr_scopes(expr, scopes, scope); | 245 | if let Some(expr) = initializer { |
246 | scopes.set_scope(*expr, scope); | ||
247 | compute_expr_scopes(*expr, body, scopes, scope); | ||
199 | } | 248 | } |
200 | scope = scopes.new_scope(scope); | 249 | scope = scopes.new_scope(scope); |
201 | if let Some(pat) = stmt.pat() { | 250 | scopes.add_bindings(body, scope, *pat); |
202 | scopes.add_bindings(scope, pat); | ||
203 | } | ||
204 | } | 251 | } |
205 | ast::Stmt::ExprStmt(expr_stmt) => { | 252 | Statement::Expr(expr) => { |
206 | if let Some(expr) = expr_stmt.expr() { | 253 | scopes.set_scope(*expr, scope); |
207 | scopes.set_scope(expr.syntax(), scope); | 254 | compute_expr_scopes(*expr, body, scopes, scope); |
208 | compute_expr_scopes(expr, scopes, scope); | ||
209 | } | ||
210 | } | 255 | } |
211 | } | 256 | } |
212 | } | 257 | } |
213 | if let Some(expr) = block.expr() { | 258 | if let Some(expr) = tail { |
214 | scopes.set_scope(expr.syntax(), scope); | 259 | compute_expr_scopes(expr, body, scopes, scope); |
215 | compute_expr_scopes(expr, scopes, scope); | ||
216 | } | 260 | } |
217 | } | 261 | } |
218 | 262 | ||
219 | fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | 263 | fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut FnScopes, scope: ScopeId) { |
220 | match expr { | 264 | scopes.set_scope(expr, scope); |
221 | ast::Expr::IfExpr(e) => { | 265 | match &body[expr] { |
222 | let cond_scope = e | 266 | Expr::Block { statements, tail } => { |
223 | .condition() | 267 | 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 | } | 268 | } |
250 | ast::Expr::ForExpr(e) => { | 269 | Expr::For { |
251 | if let Some(expr) = e.iterable() { | 270 | iterable, |
252 | compute_expr_scopes(expr, scopes, scope); | 271 | pat, |
253 | } | 272 | body: body_expr, |
254 | let mut scope = scope; | 273 | } => { |
255 | if let Some(pat) = e.pat() { | 274 | 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); | 275 | let scope = scopes.new_scope(scope); |
265 | scopes.add_params_bindings(scope, e.param_list()); | 276 | scopes.add_bindings(body, scope, *pat); |
266 | if let Some(body) = e.body() { | 277 | compute_expr_scopes(*body_expr, body, scopes, scope); |
267 | scopes.set_scope(body.syntax(), scope); | ||
268 | compute_expr_scopes(body, scopes, scope); | ||
269 | } | ||
270 | } | 278 | } |
271 | ast::Expr::CallExpr(e) => { | 279 | Expr::Lambda { |
272 | compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); | 280 | args, |
273 | } | 281 | body: body_expr, |
274 | ast::Expr::MethodCallExpr(e) => { | 282 | .. |
275 | compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); | 283 | } => { |
284 | let scope = scopes.new_scope(scope); | ||
285 | scopes.add_params_bindings(scope, &args); | ||
286 | compute_expr_scopes(*body_expr, body, scopes, scope); | ||
276 | } | 287 | } |
277 | ast::Expr::MatchExpr(e) => { | 288 | Expr::Match { expr, arms } => { |
278 | if let Some(expr) = e.expr() { | 289 | compute_expr_scopes(*expr, body, scopes, scope); |
279 | compute_expr_scopes(expr, scopes, scope); | 290 | 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); | 291 | let scope = scopes.new_scope(scope); |
283 | for pat in arm.pats() { | 292 | for pat in &arm.pats { |
284 | scopes.add_bindings(scope, pat); | 293 | scopes.add_bindings(body, scope, *pat); |
285 | } | ||
286 | if let Some(expr) = arm.expr() { | ||
287 | compute_expr_scopes(expr, scopes, scope); | ||
288 | } | 294 | } |
295 | scopes.set_scope(arm.expr, scope); | ||
296 | compute_expr_scopes(arm.expr, body, scopes, scope); | ||
289 | } | 297 | } |
290 | } | 298 | } |
291 | _ => expr | 299 | 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 | }; | 300 | }; |
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 | } | 301 | } |
328 | 302 | ||
329 | #[derive(Debug)] | 303 | #[derive(Debug)] |
@@ -338,6 +312,8 @@ mod tests { | |||
338 | use ra_syntax::SourceFileNode; | 312 | use ra_syntax::SourceFileNode; |
339 | use test_utils::{extract_offset, assert_eq_text}; | 313 | use test_utils::{extract_offset, assert_eq_text}; |
340 | 314 | ||
315 | use crate::expr; | ||
316 | |||
341 | use super::*; | 317 | use super::*; |
342 | 318 | ||
343 | fn do_check(code: &str, expected: &[&str]) { | 319 | fn do_check(code: &str, expected: &[&str]) { |
@@ -353,15 +329,20 @@ mod tests { | |||
353 | let file = SourceFileNode::parse(&code); | 329 | let file = SourceFileNode::parse(&code); |
354 | let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); | 330 | 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(); | 331 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); |
356 | let scopes = FnScopes::new(fn_def); | 332 | let body_hir = expr::collect_fn_body_syntax(fn_def); |
333 | let scopes = FnScopes::new(Arc::clone(body_hir.body())); | ||
334 | let scopes = ScopesWithSyntaxMapping { | ||
335 | scopes: Arc::new(scopes), | ||
336 | syntax_mapping: Arc::new(body_hir), | ||
337 | }; | ||
357 | let actual = scopes | 338 | let actual = scopes |
358 | .scope_chain(marker.syntax()) | 339 | .scope_chain(marker.syntax()) |
359 | .flat_map(|scope| scopes.entries(scope)) | 340 | .flat_map(|scope| scopes.scopes.entries(scope)) |
360 | .map(|it| it.name().to_string()) | 341 | .map(|it| it.name().to_string()) |
361 | .collect::<Vec<_>>() | 342 | .collect::<Vec<_>>() |
362 | .join("\n"); | 343 | .join("\n"); |
363 | let expected = expected.join("\n"); | 344 | let expected = expected.join("\n"); |
364 | assert_eq_text!(&actual, &expected); | 345 | assert_eq_text!(&expected, &actual); |
365 | } | 346 | } |
366 | 347 | ||
367 | #[test] | 348 | #[test] |
@@ -389,7 +370,7 @@ mod tests { | |||
389 | } | 370 | } |
390 | 371 | ||
391 | #[test] | 372 | #[test] |
392 | fn test_metod_call_scope() { | 373 | fn test_method_call_scope() { |
393 | do_check( | 374 | do_check( |
394 | r" | 375 | r" |
395 | fn quux() { | 376 | fn quux() { |
@@ -445,10 +426,15 @@ mod tests { | |||
445 | let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); | 426 | 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(); | 427 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); |
447 | 428 | ||
448 | let scopes = FnScopes::new(fn_def); | 429 | let body_hir = expr::collect_fn_body_syntax(fn_def); |
430 | let scopes = FnScopes::new(Arc::clone(body_hir.body())); | ||
431 | let scopes = ScopesWithSyntaxMapping { | ||
432 | scopes: Arc::new(scopes), | ||
433 | syntax_mapping: Arc::new(body_hir), | ||
434 | }; | ||
449 | 435 | ||
450 | let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); | 436 | let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); |
451 | let local_name = local_name_entry.ptr().resolve(&file); | 437 | let local_name = local_name_entry.ptr(); |
452 | let expected_name = | 438 | let expected_name = |
453 | find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | 439 | find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); |
454 | assert_eq!(local_name.range(), expected_name.syntax().range()); | 440 | assert_eq!(local_name.range(), expected_name.syntax().range()); |