aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/body
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/body')
-rw-r--r--crates/hir_def/src/body/lower.rs931
-rw-r--r--crates/hir_def/src/body/scope.rs456
2 files changed, 1387 insertions, 0 deletions
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
new file mode 100644
index 000000000..a26251cde
--- /dev/null
+++ b/crates/hir_def/src/body/lower.rs
@@ -0,0 +1,931 @@
1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
2//! representation.
3
4use std::{any::type_name, sync::Arc};
5
6use arena::Arena;
7use either::Either;
8use hir_expand::{
9 hygiene::Hygiene,
10 name::{name, AsName, Name},
11 HirFileId, MacroDefId, MacroDefKind,
12};
13use rustc_hash::FxHashMap;
14use syntax::{
15 ast::{
16 self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner,
17 SlicePatComponents,
18 },
19 AstNode, AstPtr,
20};
21use test_utils::mark;
22
23use crate::{
24 adt::StructKind,
25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax},
26 builtin_type::{BuiltinFloat, BuiltinInt},
27 db::DefDatabase,
28 expr::{
29 dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal,
30 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
31 },
32 item_scope::BuiltinShadowMode,
33 item_tree::{ItemTree, ItemTreeId, ItemTreeNode},
34 path::{GenericArgs, Path},
35 type_ref::{Mutability, Rawness, TypeRef},
36 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
37 StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
38};
39
40use super::{ExprSource, PatSource};
41
42pub(crate) struct LowerCtx {
43 hygiene: Hygiene,
44}
45
46impl LowerCtx {
47 pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
48 LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) }
49 }
50 pub fn with_hygiene(hygiene: &Hygiene) -> Self {
51 LowerCtx { hygiene: hygiene.clone() }
52 }
53
54 pub fn lower_path(&self, ast: ast::Path) -> Option<Path> {
55 Path::from_src(ast, &self.hygiene)
56 }
57}
58
59pub(super) fn lower(
60 db: &dyn DefDatabase,
61 def: DefWithBodyId,
62 expander: Expander,
63 params: Option<ast::ParamList>,
64 body: Option<ast::Expr>,
65) -> (Body, BodySourceMap) {
66 let item_tree = db.item_tree(expander.current_file_id);
67 ExprCollector {
68 db,
69 def,
70 source_map: BodySourceMap::default(),
71 body: Body {
72 exprs: Arena::default(),
73 pats: Arena::default(),
74 params: Vec::new(),
75 body_expr: dummy_expr_id(),
76 item_scope: Default::default(),
77 },
78 item_trees: {
79 let mut map = FxHashMap::default();
80 map.insert(expander.current_file_id, item_tree);
81 map
82 },
83 expander,
84 }
85 .collect(params, body)
86}
87
88struct ExprCollector<'a> {
89 db: &'a dyn DefDatabase,
90 def: DefWithBodyId,
91 expander: Expander,
92 body: Body,
93 source_map: BodySourceMap,
94
95 item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
96}
97
98impl ExprCollector<'_> {
99 fn collect(
100 mut self,
101 param_list: Option<ast::ParamList>,
102 body: Option<ast::Expr>,
103 ) -> (Body, BodySourceMap) {
104 if let Some(param_list) = param_list {
105 if let Some(self_param) = param_list.self_param() {
106 let ptr = AstPtr::new(&self_param);
107 let param_pat = self.alloc_pat(
108 Pat::Bind {
109 name: name![self],
110 mode: BindingAnnotation::Unannotated,
111 subpat: None,
112 },
113 Either::Right(ptr),
114 );
115 self.body.params.push(param_pat);
116 }
117
118 for param in param_list.params() {
119 let pat = match param.pat() {
120 None => continue,
121 Some(pat) => pat,
122 };
123 let param_pat = self.collect_pat(pat);
124 self.body.params.push(param_pat);
125 }
126 };
127
128 self.body.body_expr = self.collect_expr_opt(body);
129 (self.body, self.source_map)
130 }
131
132 fn ctx(&self) -> LowerCtx {
133 LowerCtx::new(self.db, self.expander.current_file_id)
134 }
135
136 fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
137 let src = self.expander.to_source(ptr);
138 let id = self.make_expr(expr, Ok(src.clone()));
139 self.source_map.expr_map.insert(src, id);
140 id
141 }
142 // desugared exprs don't have ptr, that's wrong and should be fixed
143 // somehow.
144 fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
145 self.make_expr(expr, Err(SyntheticSyntax))
146 }
147 fn empty_block(&mut self) -> ExprId {
148 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None })
149 }
150 fn missing_expr(&mut self) -> ExprId {
151 self.alloc_expr_desugared(Expr::Missing)
152 }
153 fn make_expr(&mut self, expr: Expr, src: Result<ExprSource, SyntheticSyntax>) -> ExprId {
154 let id = self.body.exprs.alloc(expr);
155 self.source_map.expr_map_back.insert(id, src);
156 id
157 }
158
159 fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
160 let src = self.expander.to_source(ptr);
161 let id = self.make_pat(pat, Ok(src.clone()));
162 self.source_map.pat_map.insert(src, id);
163 id
164 }
165 fn missing_pat(&mut self) -> PatId {
166 self.make_pat(Pat::Missing, Err(SyntheticSyntax))
167 }
168 fn make_pat(&mut self, pat: Pat, src: Result<PatSource, SyntheticSyntax>) -> PatId {
169 let id = self.body.pats.alloc(pat);
170 self.source_map.pat_map_back.insert(id, src);
171 id
172 }
173
174 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
175 let syntax_ptr = AstPtr::new(&expr);
176 if !self.expander.is_cfg_enabled(&expr) {
177 return self.missing_expr();
178 }
179
180 match expr {
181 ast::Expr::IfExpr(e) => {
182 let then_branch = self.collect_block_opt(e.then_branch());
183
184 let else_branch = e.else_branch().map(|b| match b {
185 ast::ElseBranch::Block(it) => self.collect_block(it),
186 ast::ElseBranch::IfExpr(elif) => {
187 let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap();
188 self.collect_expr(expr)
189 }
190 });
191
192 let condition = match e.condition() {
193 None => self.missing_expr(),
194 Some(condition) => match condition.pat() {
195 None => self.collect_expr_opt(condition.expr()),
196 // if let -- desugar to match
197 Some(pat) => {
198 let pat = self.collect_pat(pat);
199 let match_expr = self.collect_expr_opt(condition.expr());
200 let placeholder_pat = self.missing_pat();
201 let arms = vec![
202 MatchArm { pat, expr: then_branch, guard: None },
203 MatchArm {
204 pat: placeholder_pat,
205 expr: else_branch.unwrap_or_else(|| self.empty_block()),
206 guard: None,
207 },
208 ];
209 return self
210 .alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr);
211 }
212 },
213 };
214
215 self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
216 }
217 ast::Expr::EffectExpr(e) => match e.effect() {
218 ast::Effect::Try(_) => {
219 let body = self.collect_block_opt(e.block_expr());
220 self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
221 }
222 ast::Effect::Unsafe(_) => {
223 let body = self.collect_block_opt(e.block_expr());
224 self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
225 }
226 // FIXME: we need to record these effects somewhere...
227 ast::Effect::Label(label) => match e.block_expr() {
228 Some(block) => {
229 let res = self.collect_block(block);
230 match &mut self.body.exprs[res] {
231 Expr::Block { label: block_label, .. } => {
232 *block_label =
233 label.lifetime_token().map(|t| Name::new_lifetime(&t))
234 }
235 _ => unreachable!(),
236 }
237 res
238 }
239 None => self.missing_expr(),
240 },
241 // FIXME: we need to record these effects somewhere...
242 ast::Effect::Async(_) => self.collect_block_opt(e.block_expr()),
243 },
244 ast::Expr::BlockExpr(e) => self.collect_block(e),
245 ast::Expr::LoopExpr(e) => {
246 let body = self.collect_block_opt(e.loop_body());
247 self.alloc_expr(
248 Expr::Loop {
249 body,
250 label: e
251 .label()
252 .and_then(|l| l.lifetime_token())
253 .map(|l| Name::new_lifetime(&l)),
254 },
255 syntax_ptr,
256 )
257 }
258 ast::Expr::WhileExpr(e) => {
259 let body = self.collect_block_opt(e.loop_body());
260
261 let condition = match e.condition() {
262 None => self.missing_expr(),
263 Some(condition) => match condition.pat() {
264 None => self.collect_expr_opt(condition.expr()),
265 // if let -- desugar to match
266 Some(pat) => {
267 mark::hit!(infer_resolve_while_let);
268 let pat = self.collect_pat(pat);
269 let match_expr = self.collect_expr_opt(condition.expr());
270 let placeholder_pat = self.missing_pat();
271 let break_ =
272 self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
273 let arms = vec![
274 MatchArm { pat, expr: body, guard: None },
275 MatchArm { pat: placeholder_pat, expr: break_, guard: None },
276 ];
277 let match_expr =
278 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
279 return self.alloc_expr(
280 Expr::Loop {
281 body: match_expr,
282 label: e
283 .label()
284 .and_then(|l| l.lifetime_token())
285 .map(|l| Name::new_lifetime(&l)),
286 },
287 syntax_ptr,
288 );
289 }
290 },
291 };
292
293 self.alloc_expr(
294 Expr::While {
295 condition,
296 body,
297 label: e
298 .label()
299 .and_then(|l| l.lifetime_token())
300 .map(|l| Name::new_lifetime(&l)),
301 },
302 syntax_ptr,
303 )
304 }
305 ast::Expr::ForExpr(e) => {
306 let iterable = self.collect_expr_opt(e.iterable());
307 let pat = self.collect_pat_opt(e.pat());
308 let body = self.collect_block_opt(e.loop_body());
309 self.alloc_expr(
310 Expr::For {
311 iterable,
312 pat,
313 body,
314 label: e
315 .label()
316 .and_then(|l| l.lifetime_token())
317 .map(|l| Name::new_lifetime(&l)),
318 },
319 syntax_ptr,
320 )
321 }
322 ast::Expr::CallExpr(e) => {
323 let callee = self.collect_expr_opt(e.expr());
324 let args = if let Some(arg_list) = e.arg_list() {
325 arg_list.args().map(|e| self.collect_expr(e)).collect()
326 } else {
327 Vec::new()
328 };
329 self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
330 }
331 ast::Expr::MethodCallExpr(e) => {
332 let receiver = self.collect_expr_opt(e.expr());
333 let args = if let Some(arg_list) = e.arg_list() {
334 arg_list.args().map(|e| self.collect_expr(e)).collect()
335 } else {
336 Vec::new()
337 };
338 let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
339 let generic_args =
340 e.generic_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it));
341 self.alloc_expr(
342 Expr::MethodCall { receiver, method_name, args, generic_args },
343 syntax_ptr,
344 )
345 }
346 ast::Expr::MatchExpr(e) => {
347 let expr = self.collect_expr_opt(e.expr());
348 let arms = if let Some(match_arm_list) = e.match_arm_list() {
349 match_arm_list
350 .arms()
351 .map(|arm| MatchArm {
352 pat: self.collect_pat_opt(arm.pat()),
353 expr: self.collect_expr_opt(arm.expr()),
354 guard: arm
355 .guard()
356 .and_then(|guard| guard.expr())
357 .map(|e| self.collect_expr(e)),
358 })
359 .collect()
360 } else {
361 Vec::new()
362 };
363 self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
364 }
365 ast::Expr::PathExpr(e) => {
366 let path = e
367 .path()
368 .and_then(|path| self.expander.parse_path(path))
369 .map(Expr::Path)
370 .unwrap_or(Expr::Missing);
371 self.alloc_expr(path, syntax_ptr)
372 }
373 ast::Expr::ContinueExpr(e) => self.alloc_expr(
374 Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
375 syntax_ptr,
376 ),
377 ast::Expr::BreakExpr(e) => {
378 let expr = e.expr().map(|e| self.collect_expr(e));
379 self.alloc_expr(
380 Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
381 syntax_ptr,
382 )
383 }
384 ast::Expr::ParenExpr(e) => {
385 let inner = self.collect_expr_opt(e.expr());
386 // make the paren expr point to the inner expression as well
387 let src = self.expander.to_source(syntax_ptr);
388 self.source_map.expr_map.insert(src, inner);
389 inner
390 }
391 ast::Expr::ReturnExpr(e) => {
392 let expr = e.expr().map(|e| self.collect_expr(e));
393 self.alloc_expr(Expr::Return { expr }, syntax_ptr)
394 }
395 ast::Expr::RecordExpr(e) => {
396 let path = e.path().and_then(|path| self.expander.parse_path(path));
397 let mut field_ptrs = Vec::new();
398 let record_lit = if let Some(nfl) = e.record_expr_field_list() {
399 let fields = nfl
400 .fields()
401 .inspect(|field| field_ptrs.push(AstPtr::new(field)))
402 .filter_map(|field| {
403 if !self.expander.is_cfg_enabled(&field) {
404 return None;
405 }
406 let name = field.field_name()?.as_name();
407
408 Some(RecordLitField {
409 name,
410 expr: match field.expr() {
411 Some(e) => self.collect_expr(e),
412 None => self.missing_expr(),
413 },
414 })
415 })
416 .collect();
417 let spread = nfl.spread().map(|s| self.collect_expr(s));
418 Expr::RecordLit { path, fields, spread }
419 } else {
420 Expr::RecordLit { path, fields: Vec::new(), spread: None }
421 };
422
423 let res = self.alloc_expr(record_lit, syntax_ptr);
424 for (i, ptr) in field_ptrs.into_iter().enumerate() {
425 let src = self.expander.to_source(ptr);
426 self.source_map.field_map.insert((res, i), src);
427 }
428 res
429 }
430 ast::Expr::FieldExpr(e) => {
431 let expr = self.collect_expr_opt(e.expr());
432 let name = match e.field_access() {
433 Some(kind) => kind.as_name(),
434 _ => Name::missing(),
435 };
436 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr)
437 }
438 ast::Expr::AwaitExpr(e) => {
439 let expr = self.collect_expr_opt(e.expr());
440 self.alloc_expr(Expr::Await { expr }, syntax_ptr)
441 }
442 ast::Expr::TryExpr(e) => {
443 let expr = self.collect_expr_opt(e.expr());
444 self.alloc_expr(Expr::Try { expr }, syntax_ptr)
445 }
446 ast::Expr::CastExpr(e) => {
447 let expr = self.collect_expr_opt(e.expr());
448 let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty());
449 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
450 }
451 ast::Expr::RefExpr(e) => {
452 let expr = self.collect_expr_opt(e.expr());
453 let raw_tok = e.raw_token().is_some();
454 let mutability = if raw_tok {
455 if e.mut_token().is_some() {
456 Mutability::Mut
457 } else if e.const_token().is_some() {
458 Mutability::Shared
459 } else {
460 unreachable!("parser only remaps to raw_token() if matching mutability token follows")
461 }
462 } else {
463 Mutability::from_mutable(e.mut_token().is_some())
464 };
465 let rawness = Rawness::from_raw(raw_tok);
466 self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr)
467 }
468 ast::Expr::PrefixExpr(e) => {
469 let expr = self.collect_expr_opt(e.expr());
470 if let Some(op) = e.op_kind() {
471 self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
472 } else {
473 self.alloc_expr(Expr::Missing, syntax_ptr)
474 }
475 }
476 ast::Expr::ClosureExpr(e) => {
477 let mut args = Vec::new();
478 let mut arg_types = Vec::new();
479 if let Some(pl) = e.param_list() {
480 for param in pl.params() {
481 let pat = self.collect_pat_opt(param.pat());
482 let type_ref = param.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
483 args.push(pat);
484 arg_types.push(type_ref);
485 }
486 }
487 let ret_type =
488 e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&self.ctx(), it));
489 let body = self.collect_expr_opt(e.body());
490 self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
491 }
492 ast::Expr::BinExpr(e) => {
493 let lhs = self.collect_expr_opt(e.lhs());
494 let rhs = self.collect_expr_opt(e.rhs());
495 let op = e.op_kind().map(BinaryOp::from);
496 self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
497 }
498 ast::Expr::TupleExpr(e) => {
499 let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
500 self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr)
501 }
502 ast::Expr::BoxExpr(e) => {
503 let expr = self.collect_expr_opt(e.expr());
504 self.alloc_expr(Expr::Box { expr }, syntax_ptr)
505 }
506
507 ast::Expr::ArrayExpr(e) => {
508 let kind = e.kind();
509
510 match kind {
511 ArrayExprKind::ElementList(e) => {
512 let exprs = e.map(|expr| self.collect_expr(expr)).collect();
513 self.alloc_expr(Expr::Array(Array::ElementList(exprs)), syntax_ptr)
514 }
515 ArrayExprKind::Repeat { initializer, repeat } => {
516 let initializer = self.collect_expr_opt(initializer);
517 let repeat = self.collect_expr_opt(repeat);
518 self.alloc_expr(
519 Expr::Array(Array::Repeat { initializer, repeat }),
520 syntax_ptr,
521 )
522 }
523 }
524 }
525
526 ast::Expr::Literal(e) => self.alloc_expr(Expr::Literal(e.kind().into()), syntax_ptr),
527 ast::Expr::IndexExpr(e) => {
528 let base = self.collect_expr_opt(e.base());
529 let index = self.collect_expr_opt(e.index());
530 self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
531 }
532 ast::Expr::RangeExpr(e) => {
533 let lhs = e.start().map(|lhs| self.collect_expr(lhs));
534 let rhs = e.end().map(|rhs| self.collect_expr(rhs));
535 match e.op_kind() {
536 Some(range_type) => {
537 self.alloc_expr(Expr::Range { lhs, rhs, range_type }, syntax_ptr)
538 }
539 None => self.alloc_expr(Expr::Missing, syntax_ptr),
540 }
541 }
542 ast::Expr::MacroCall(e) => {
543 if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) {
544 let mac = MacroDefId {
545 krate: Some(self.expander.module.krate),
546 ast_id: Some(self.expander.ast_id(&e)),
547 kind: MacroDefKind::Declarative,
548 local_inner: false,
549 };
550 self.body.item_scope.define_legacy_macro(name, mac);
551
552 // FIXME: do we still need to allocate this as missing ?
553 self.alloc_expr(Expr::Missing, syntax_ptr)
554 } else {
555 let macro_call = self.expander.to_source(AstPtr::new(&e));
556 match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) {
557 Some((mark, expansion)) => {
558 self.source_map
559 .expansions
560 .insert(macro_call, self.expander.current_file_id);
561
562 let item_tree = self.db.item_tree(self.expander.current_file_id);
563 self.item_trees.insert(self.expander.current_file_id, item_tree);
564 let id = self.collect_expr(expansion);
565 self.expander.exit(self.db, mark);
566 id
567 }
568 None => self.alloc_expr(Expr::Missing, syntax_ptr),
569 }
570 }
571 }
572 }
573 }
574
575 fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> {
576 let id = self.expander.ast_id(ast);
577 let tree = &self.item_trees[&id.file_id];
578
579 // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
580
581 // Root file (non-macro).
582 let item_tree_id = tree
583 .all_inner_items()
584 .chain(tree.top_level_items().iter().copied())
585 .filter_map(|mod_item| mod_item.downcast::<N>())
586 .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast())
587 .or_else(|| {
588 log::debug!(
589 "couldn't find inner {} item for {:?} (AST: `{}` - {:?})",
590 type_name::<N>(),
591 id,
592 ast.syntax(),
593 ast.syntax(),
594 );
595 None
596 })?;
597
598 Some(ItemTreeId::new(id.file_id, item_tree_id))
599 }
600
601 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
602 if let Some(expr) = expr {
603 self.collect_expr(expr)
604 } else {
605 self.missing_expr()
606 }
607 }
608
609 fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
610 let syntax_node_ptr = AstPtr::new(&block.clone().into());
611 self.collect_block_items(&block);
612 let statements = block
613 .statements()
614 .filter_map(|s| {
615 let stmt = match s {
616 ast::Stmt::LetStmt(stmt) => {
617 let pat = self.collect_pat_opt(stmt.pat());
618 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
619 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
620 Statement::Let { pat, type_ref, initializer }
621 }
622 ast::Stmt::ExprStmt(stmt) => {
623 Statement::Expr(self.collect_expr_opt(stmt.expr()))
624 }
625 ast::Stmt::Item(_) => return None,
626 };
627 Some(stmt)
628 })
629 .collect();
630 let tail = block.expr().map(|e| self.collect_expr(e));
631 self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr)
632 }
633
634 fn collect_block_items(&mut self, block: &ast::BlockExpr) {
635 let container = ContainerId::DefWithBodyId(self.def);
636
637 let items = block
638 .statements()
639 .filter_map(|stmt| match stmt {
640 ast::Stmt::Item(it) => Some(it),
641 ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None,
642 })
643 .filter_map(|item| {
644 let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
645 ast::Item::Fn(def) => {
646 let id = self.find_inner_item(&def)?;
647 (
648 FunctionLoc { container: container.into(), id }.intern(self.db).into(),
649 def.name(),
650 )
651 }
652 ast::Item::TypeAlias(def) => {
653 let id = self.find_inner_item(&def)?;
654 (
655 TypeAliasLoc { container: container.into(), id }.intern(self.db).into(),
656 def.name(),
657 )
658 }
659 ast::Item::Const(def) => {
660 let id = self.find_inner_item(&def)?;
661 (
662 ConstLoc { container: container.into(), id }.intern(self.db).into(),
663 def.name(),
664 )
665 }
666 ast::Item::Static(def) => {
667 let id = self.find_inner_item(&def)?;
668 (StaticLoc { container, id }.intern(self.db).into(), def.name())
669 }
670 ast::Item::Struct(def) => {
671 let id = self.find_inner_item(&def)?;
672 (StructLoc { container, id }.intern(self.db).into(), def.name())
673 }
674 ast::Item::Enum(def) => {
675 let id = self.find_inner_item(&def)?;
676 (EnumLoc { container, id }.intern(self.db).into(), def.name())
677 }
678 ast::Item::Union(def) => {
679 let id = self.find_inner_item(&def)?;
680 (UnionLoc { container, id }.intern(self.db).into(), def.name())
681 }
682 ast::Item::Trait(def) => {
683 let id = self.find_inner_item(&def)?;
684 (TraitLoc { container, id }.intern(self.db).into(), def.name())
685 }
686 ast::Item::ExternBlock(_) => return None, // FIXME: collect from extern blocks
687 ast::Item::Impl(_)
688 | ast::Item::Use(_)
689 | ast::Item::ExternCrate(_)
690 | ast::Item::Module(_)
691 | ast::Item::MacroCall(_) => return None,
692 };
693
694 Some((def, name))
695 })
696 .collect::<Vec<_>>();
697
698 for (def, name) in items {
699 self.body.item_scope.define_def(def);
700 if let Some(name) = name {
701 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
702 let has_constructor = match def {
703 ModuleDefId::AdtId(AdtId::StructId(s)) => {
704 self.db.struct_data(s).variant_data.kind() != StructKind::Record
705 }
706 _ => true,
707 };
708 self.body.item_scope.push_res(
709 name.as_name(),
710 crate::per_ns::PerNs::from_def(def, vis, has_constructor),
711 );
712 }
713 }
714 }
715
716 fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId {
717 if let Some(block) = expr {
718 self.collect_block(block)
719 } else {
720 self.missing_expr()
721 }
722 }
723
724 fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
725 let pattern = match &pat {
726 ast::Pat::IdentPat(bp) => {
727 let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
728 let annotation =
729 BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
730 let subpat = bp.pat().map(|subpat| self.collect_pat(subpat));
731 if annotation == BindingAnnotation::Unannotated && subpat.is_none() {
732 // This could also be a single-segment path pattern. To
733 // decide that, we need to try resolving the name.
734 let (resolved, _) = self.expander.crate_def_map.resolve_path(
735 self.db,
736 self.expander.module.local_id,
737 &name.clone().into(),
738 BuiltinShadowMode::Other,
739 );
740 match resolved.take_values() {
741 Some(ModuleDefId::ConstId(_)) => Pat::Path(name.into()),
742 Some(ModuleDefId::EnumVariantId(_)) => {
743 // this is only really valid for unit variants, but
744 // shadowing other enum variants with a pattern is
745 // an error anyway
746 Pat::Path(name.into())
747 }
748 Some(ModuleDefId::AdtId(AdtId::StructId(s)))
749 if self.db.struct_data(s).variant_data.kind() != StructKind::Record =>
750 {
751 // Funnily enough, record structs *can* be shadowed
752 // by pattern bindings (but unit or tuple structs
753 // can't).
754 Pat::Path(name.into())
755 }
756 // shadowing statics is an error as well, so we just ignore that case here
757 _ => Pat::Bind { name, mode: annotation, subpat },
758 }
759 } else {
760 Pat::Bind { name, mode: annotation, subpat }
761 }
762 }
763 ast::Pat::TupleStructPat(p) => {
764 let path = p.path().and_then(|path| self.expander.parse_path(path));
765 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
766 Pat::TupleStruct { path, args, ellipsis }
767 }
768 ast::Pat::RefPat(p) => {
769 let pat = self.collect_pat_opt(p.pat());
770 let mutability = Mutability::from_mutable(p.mut_token().is_some());
771 Pat::Ref { pat, mutability }
772 }
773 ast::Pat::PathPat(p) => {
774 let path = p.path().and_then(|path| self.expander.parse_path(path));
775 path.map(Pat::Path).unwrap_or(Pat::Missing)
776 }
777 ast::Pat::OrPat(p) => {
778 let pats = p.pats().map(|p| self.collect_pat(p)).collect();
779 Pat::Or(pats)
780 }
781 ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat()),
782 ast::Pat::TuplePat(p) => {
783 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
784 Pat::Tuple { args, ellipsis }
785 }
786 ast::Pat::WildcardPat(_) => Pat::Wild,
787 ast::Pat::RecordPat(p) => {
788 let path = p.path().and_then(|path| self.expander.parse_path(path));
789 let args: Vec<_> = p
790 .record_pat_field_list()
791 .expect("every struct should have a field list")
792 .fields()
793 .filter_map(|f| {
794 let ast_pat = f.pat()?;
795 let pat = self.collect_pat(ast_pat);
796 let name = f.field_name()?.as_name();
797 Some(RecordFieldPat { name, pat })
798 })
799 .collect();
800
801 let ellipsis = p
802 .record_pat_field_list()
803 .expect("every struct should have a field list")
804 .dotdot_token()
805 .is_some();
806
807 Pat::Record { path, args, ellipsis }
808 }
809 ast::Pat::SlicePat(p) => {
810 let SlicePatComponents { prefix, slice, suffix } = p.components();
811
812 // FIXME properly handle `RestPat`
813 Pat::Slice {
814 prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(),
815 slice: slice.map(|p| self.collect_pat(p)),
816 suffix: suffix.into_iter().map(|p| self.collect_pat(p)).collect(),
817 }
818 }
819 ast::Pat::LiteralPat(lit) => {
820 if let Some(ast_lit) = lit.literal() {
821 let expr = Expr::Literal(ast_lit.kind().into());
822 let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
823 let expr_id = self.alloc_expr(expr, expr_ptr);
824 Pat::Lit(expr_id)
825 } else {
826 Pat::Missing
827 }
828 }
829 ast::Pat::RestPat(_) => {
830 // `RestPat` requires special handling and should not be mapped
831 // to a Pat. Here we are using `Pat::Missing` as a fallback for
832 // when `RestPat` is mapped to `Pat`, which can easily happen
833 // when the source code being analyzed has a malformed pattern
834 // which includes `..` in a place where it isn't valid.
835
836 Pat::Missing
837 }
838 // FIXME: implement
839 ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
840 };
841 let ptr = AstPtr::new(&pat);
842 self.alloc_pat(pattern, Either::Left(ptr))
843 }
844
845 fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
846 if let Some(pat) = pat {
847 self.collect_pat(pat)
848 } else {
849 self.missing_pat()
850 }
851 }
852
853 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) {
854 // Find the location of the `..`, if there is one. Note that we do not
855 // consider the possiblity of there being multiple `..` here.
856 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
857 // We want to skip the `..` pattern here, since we account for it above.
858 let args = args
859 .filter(|p| !matches!(p, ast::Pat::RestPat(_)))
860 .map(|p| self.collect_pat(p))
861 .collect();
862
863 (args, ellipsis)
864 }
865}
866
867impl From<ast::BinOp> for BinaryOp {
868 fn from(ast_op: ast::BinOp) -> Self {
869 match ast_op {
870 ast::BinOp::BooleanOr => BinaryOp::LogicOp(LogicOp::Or),
871 ast::BinOp::BooleanAnd => BinaryOp::LogicOp(LogicOp::And),
872 ast::BinOp::EqualityTest => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
873 ast::BinOp::NegatedEqualityTest => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
874 ast::BinOp::LesserEqualTest => {
875 BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: false })
876 }
877 ast::BinOp::GreaterEqualTest => {
878 BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false })
879 }
880 ast::BinOp::LesserTest => {
881 BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: true })
882 }
883 ast::BinOp::GreaterTest => {
884 BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true })
885 }
886 ast::BinOp::Addition => BinaryOp::ArithOp(ArithOp::Add),
887 ast::BinOp::Multiplication => BinaryOp::ArithOp(ArithOp::Mul),
888 ast::BinOp::Subtraction => BinaryOp::ArithOp(ArithOp::Sub),
889 ast::BinOp::Division => BinaryOp::ArithOp(ArithOp::Div),
890 ast::BinOp::Remainder => BinaryOp::ArithOp(ArithOp::Rem),
891 ast::BinOp::LeftShift => BinaryOp::ArithOp(ArithOp::Shl),
892 ast::BinOp::RightShift => BinaryOp::ArithOp(ArithOp::Shr),
893 ast::BinOp::BitwiseXor => BinaryOp::ArithOp(ArithOp::BitXor),
894 ast::BinOp::BitwiseOr => BinaryOp::ArithOp(ArithOp::BitOr),
895 ast::BinOp::BitwiseAnd => BinaryOp::ArithOp(ArithOp::BitAnd),
896 ast::BinOp::Assignment => BinaryOp::Assignment { op: None },
897 ast::BinOp::AddAssign => BinaryOp::Assignment { op: Some(ArithOp::Add) },
898 ast::BinOp::DivAssign => BinaryOp::Assignment { op: Some(ArithOp::Div) },
899 ast::BinOp::MulAssign => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
900 ast::BinOp::RemAssign => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
901 ast::BinOp::ShlAssign => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
902 ast::BinOp::ShrAssign => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
903 ast::BinOp::SubAssign => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
904 ast::BinOp::BitOrAssign => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
905 ast::BinOp::BitAndAssign => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },
906 ast::BinOp::BitXorAssign => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
907 }
908 }
909}
910
911impl From<ast::LiteralKind> for Literal {
912 fn from(ast_lit_kind: ast::LiteralKind) -> Self {
913 match ast_lit_kind {
914 LiteralKind::IntNumber { suffix } => {
915 let known_name = suffix.and_then(|it| BuiltinInt::from_suffix(&it));
916
917 Literal::Int(Default::default(), known_name)
918 }
919 LiteralKind::FloatNumber { suffix } => {
920 let known_name = suffix.and_then(|it| BuiltinFloat::from_suffix(&it));
921
922 Literal::Float(Default::default(), known_name)
923 }
924 LiteralKind::ByteString => Literal::ByteString(Default::default()),
925 LiteralKind::String => Literal::String(Default::default()),
926 LiteralKind::Byte => Literal::Int(Default::default(), Some(BuiltinInt::U8)),
927 LiteralKind::Bool(val) => Literal::Bool(val),
928 LiteralKind::Char => Literal::Char(Default::default()),
929 }
930 }
931}
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
new file mode 100644
index 000000000..9142bc05b
--- /dev/null
+++ b/crates/hir_def/src/body/scope.rs
@@ -0,0 +1,456 @@
1//! Name resolution for expressions.
2use std::sync::Arc;
3
4use arena::{Arena, Idx};
5use hir_expand::name::Name;
6use rustc_hash::FxHashMap;
7
8use crate::{
9 body::Body,
10 db::DefDatabase,
11 expr::{Expr, ExprId, Pat, PatId, Statement},
12 DefWithBodyId,
13};
14
15pub type ScopeId = Idx<ScopeData>;
16
17#[derive(Debug, PartialEq, Eq)]
18pub struct ExprScopes {
19 scopes: Arena<ScopeData>,
20 scope_by_expr: FxHashMap<ExprId, ScopeId>,
21}
22
23#[derive(Debug, PartialEq, Eq)]
24pub struct ScopeEntry {
25 name: Name,
26 pat: PatId,
27}
28
29impl ScopeEntry {
30 pub fn name(&self) -> &Name {
31 &self.name
32 }
33
34 pub fn pat(&self) -> PatId {
35 self.pat
36 }
37}
38
39#[derive(Debug, PartialEq, Eq)]
40pub struct ScopeData {
41 parent: Option<ScopeId>,
42 entries: Vec<ScopeEntry>,
43}
44
45impl ExprScopes {
46 pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> {
47 let body = db.body(def);
48 Arc::new(ExprScopes::new(&*body))
49 }
50
51 fn new(body: &Body) -> ExprScopes {
52 let mut scopes =
53 ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
54 let root = scopes.root_scope();
55 scopes.add_params_bindings(body, root, &body.params);
56 compute_expr_scopes(body.body_expr, body, &mut scopes, root);
57 scopes
58 }
59
60 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
61 &self.scopes[scope].entries
62 }
63
64 pub fn scope_chain(&self, scope: Option<ScopeId>) -> impl Iterator<Item = ScopeId> + '_ {
65 std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
66 }
67
68 pub fn resolve_name_in_scope(&self, scope: ScopeId, name: &Name) -> Option<&ScopeEntry> {
69 self.scope_chain(Some(scope))
70 .find_map(|scope| self.entries(scope).iter().find(|it| it.name == *name))
71 }
72
73 pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
74 self.scope_by_expr.get(&expr).copied()
75 }
76
77 pub fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> {
78 &self.scope_by_expr
79 }
80
81 fn root_scope(&mut self) -> ScopeId {
82 self.scopes.alloc(ScopeData { parent: None, entries: vec![] })
83 }
84
85 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
86 self.scopes.alloc(ScopeData { parent: Some(parent), entries: vec![] })
87 }
88
89 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
90 let pattern = &body[pat];
91 if let Pat::Bind { name, .. } = pattern {
92 let entry = ScopeEntry { name: name.clone(), pat };
93 self.scopes[scope].entries.push(entry);
94 }
95
96 pattern.walk_child_pats(|pat| self.add_bindings(body, scope, pat));
97 }
98
99 fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
100 params.iter().for_each(|pat| self.add_bindings(body, scope, *pat));
101 }
102
103 fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
104 self.scope_by_expr.insert(node, scope);
105 }
106}
107
108fn compute_block_scopes(
109 statements: &[Statement],
110 tail: Option<ExprId>,
111 body: &Body,
112 scopes: &mut ExprScopes,
113 mut scope: ScopeId,
114) {
115 for stmt in statements {
116 match stmt {
117 Statement::Let { pat, initializer, .. } => {
118 if let Some(expr) = initializer {
119 scopes.set_scope(*expr, scope);
120 compute_expr_scopes(*expr, body, scopes, scope);
121 }
122 scope = scopes.new_scope(scope);
123 scopes.add_bindings(body, scope, *pat);
124 }
125 Statement::Expr(expr) => {
126 scopes.set_scope(*expr, scope);
127 compute_expr_scopes(*expr, body, scopes, scope);
128 }
129 }
130 }
131 if let Some(expr) = tail {
132 compute_expr_scopes(expr, body, scopes, scope);
133 }
134}
135
136fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
137 scopes.set_scope(expr, scope);
138 match &body[expr] {
139 Expr::Block { statements, tail, .. } => {
140 compute_block_scopes(&statements, *tail, body, scopes, scope);
141 }
142 Expr::For { iterable, pat, body: body_expr, .. } => {
143 compute_expr_scopes(*iterable, body, scopes, scope);
144 let scope = scopes.new_scope(scope);
145 scopes.add_bindings(body, scope, *pat);
146 compute_expr_scopes(*body_expr, body, scopes, scope);
147 }
148 Expr::Lambda { args, body: body_expr, .. } => {
149 let scope = scopes.new_scope(scope);
150 scopes.add_params_bindings(body, scope, &args);
151 compute_expr_scopes(*body_expr, body, scopes, scope);
152 }
153 Expr::Match { expr, arms } => {
154 compute_expr_scopes(*expr, body, scopes, scope);
155 for arm in arms {
156 let scope = scopes.new_scope(scope);
157 scopes.add_bindings(body, scope, arm.pat);
158 if let Some(guard) = arm.guard {
159 scopes.set_scope(guard, scope);
160 compute_expr_scopes(guard, body, scopes, scope);
161 }
162 scopes.set_scope(arm.expr, scope);
163 compute_expr_scopes(arm.expr, body, scopes, scope);
164 }
165 }
166 e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
167 };
168}
169
170#[cfg(test)]
171mod tests {
172 use base_db::{fixture::WithFixture, FileId, SourceDatabase};
173 use hir_expand::{name::AsName, InFile};
174 use syntax::{algo::find_node_at_offset, ast, AstNode};
175 use test_utils::{assert_eq_text, extract_offset, mark};
176
177 use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId};
178
179 fn find_function(db: &TestDB, file_id: FileId) -> FunctionId {
180 let krate = db.test_crate();
181 let crate_def_map = db.crate_def_map(krate);
182
183 let module = crate_def_map.modules_for_file(file_id).next().unwrap();
184 let (_, def) = crate_def_map[module].scope.entries().next().unwrap();
185 match def.take_values().unwrap() {
186 ModuleDefId::FunctionId(it) => it,
187 _ => panic!(),
188 }
189 }
190
191 fn do_check(ra_fixture: &str, expected: &[&str]) {
192 let (offset, code) = extract_offset(ra_fixture);
193 let code = {
194 let mut buf = String::new();
195 let off: usize = offset.into();
196 buf.push_str(&code[..off]);
197 buf.push_str("<|>marker");
198 buf.push_str(&code[off..]);
199 buf
200 };
201
202 let (db, position) = TestDB::with_position(&code);
203 let file_id = position.file_id;
204 let offset = position.offset;
205
206 let file_syntax = db.parse(file_id).syntax_node();
207 let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
208 let function = find_function(&db, file_id);
209
210 let scopes = db.expr_scopes(function.into());
211 let (_body, source_map) = db.body_with_source_map(function.into());
212
213 let expr_id = source_map
214 .node_expr(InFile { file_id: file_id.into(), value: &marker.into() })
215 .unwrap();
216 let scope = scopes.scope_for(expr_id);
217
218 let actual = scopes
219 .scope_chain(scope)
220 .flat_map(|scope| scopes.entries(scope))
221 .map(|it| it.name().to_string())
222 .collect::<Vec<_>>()
223 .join("\n");
224 let expected = expected.join("\n");
225 assert_eq_text!(&expected, &actual);
226 }
227
228 #[test]
229 fn test_lambda_scope() {
230 do_check(
231 r"
232 fn quux(foo: i32) {
233 let f = |bar, baz: i32| {
234 <|>
235 };
236 }",
237 &["bar", "baz", "foo"],
238 );
239 }
240
241 #[test]
242 fn test_call_scope() {
243 do_check(
244 r"
245 fn quux() {
246 f(|x| <|> );
247 }",
248 &["x"],
249 );
250 }
251
252 #[test]
253 fn test_method_call_scope() {
254 do_check(
255 r"
256 fn quux() {
257 z.f(|x| <|> );
258 }",
259 &["x"],
260 );
261 }
262
263 #[test]
264 fn test_loop_scope() {
265 do_check(
266 r"
267 fn quux() {
268 loop {
269 let x = ();
270 <|>
271 };
272 }",
273 &["x"],
274 );
275 }
276
277 #[test]
278 fn test_match() {
279 do_check(
280 r"
281 fn quux() {
282 match () {
283 Some(x) => {
284 <|>
285 }
286 };
287 }",
288 &["x"],
289 );
290 }
291
292 #[test]
293 fn test_shadow_variable() {
294 do_check(
295 r"
296 fn foo(x: String) {
297 let x : &str = &x<|>;
298 }",
299 &["x"],
300 );
301 }
302
303 #[test]
304 fn test_bindings_after_at() {
305 do_check(
306 r"
307fn foo() {
308 match Some(()) {
309 opt @ Some(unit) => {
310 <|>
311 }
312 _ => {}
313 }
314}
315",
316 &["opt", "unit"],
317 );
318 }
319
320 #[test]
321 fn macro_inner_item() {
322 do_check(
323 r"
324 macro_rules! mac {
325 () => {{
326 fn inner() {}
327 inner();
328 }};
329 }
330
331 fn foo() {
332 mac!();
333 <|>
334 }
335 ",
336 &[],
337 );
338 }
339
340 #[test]
341 fn broken_inner_item() {
342 do_check(
343 r"
344 fn foo() {
345 trait {}
346 <|>
347 }
348 ",
349 &[],
350 );
351 }
352
353 fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
354 let (db, position) = TestDB::with_position(ra_fixture);
355 let file_id = position.file_id;
356 let offset = position.offset;
357
358 let file = db.parse(file_id).ok().unwrap();
359 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
360 .expect("failed to find a name at the target offset");
361 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap();
362
363 let function = find_function(&db, file_id);
364
365 let scopes = db.expr_scopes(function.into());
366 let (_body, source_map) = db.body_with_source_map(function.into());
367
368 let expr_scope = {
369 let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
370 let expr_id =
371 source_map.node_expr(InFile { file_id: file_id.into(), value: &expr_ast }).unwrap();
372 scopes.scope_for(expr_id).unwrap()
373 };
374
375 let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
376 let pat_src = source_map.pat_syntax(resolved.pat()).unwrap();
377
378 let local_name = pat_src.value.either(
379 |it| it.syntax_node_ptr().to_node(file.syntax()),
380 |it| it.syntax_node_ptr().to_node(file.syntax()),
381 );
382 assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
383 }
384
385 #[test]
386 fn test_resolve_local_name() {
387 do_check_local_name(
388 r#"
389fn foo(x: i32, y: u32) {
390 {
391 let z = x * 2;
392 }
393 {
394 let t = x<|> * 3;
395 }
396}
397"#,
398 7,
399 );
400 }
401
402 #[test]
403 fn test_resolve_local_name_declaration() {
404 do_check_local_name(
405 r#"
406fn foo(x: String) {
407 let x : &str = &x<|>;
408}
409"#,
410 7,
411 );
412 }
413
414 #[test]
415 fn test_resolve_local_name_shadow() {
416 do_check_local_name(
417 r"
418fn foo(x: String) {
419 let x : &str = &x;
420 x<|>
421}
422",
423 28,
424 );
425 }
426
427 #[test]
428 fn ref_patterns_contribute_bindings() {
429 do_check_local_name(
430 r"
431fn foo() {
432 if let Some(&from) = bar() {
433 from<|>;
434 }
435}
436",
437 28,
438 );
439 }
440
441 #[test]
442 fn while_let_desugaring() {
443 mark::check!(infer_resolve_while_let);
444 do_check_local_name(
445 r#"
446fn test() {
447 let foo: Option<f32> = None;
448 while let Option::Some(spam) = foo {
449 spam<|>
450 }
451}
452"#,
453 75,
454 );
455 }
456}