Skip to content

Commit b4f0403

Browse files
Handle destructuring assignments uniformly
Instead of lowering them to `<expr> = <expr>`, then hacking on-demand to resolve them, we lower them to `<pat> = <expr>`, and use the pattern infrastructure to handle them. It turns out, destructuring assignments are surprisingly similar to pattern bindings, and so only minor modifications are needed. This fixes few bugs that arose because of the non-uniform handling (for example, MIR lowering not handling slice and record patterns, and closure capture calculation not handling destructuring assignments at all), and furthermore, guarantees we won't have such bugs in the future, since the programmer will always have to explicitly handle `Expr::Assignment`. Tests don't pass yet; that's because the generated patterns do not exist in the source map. The next commit will fix that.
1 parent bac672c commit b4f0403

File tree

20 files changed

+886
-707
lines changed

20 files changed

+886
-707
lines changed

src/tools/rust-analyzer/crates/hir-def/src/body.rs

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use crate::{
2222
db::DefDatabase,
2323
expander::Expander,
2424
hir::{
25-
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
25+
dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat,
26+
PatId, RecordFieldPat, Statement,
2627
},
2728
item_tree::AttrOwner,
2829
nameres::DefMap,
@@ -286,7 +287,8 @@ impl Body {
286287
| Pat::Path(..)
287288
| Pat::ConstBlock(..)
288289
| Pat::Wild
289-
| Pat::Missing => {}
290+
| Pat::Missing
291+
| Pat::Expr(_) => {}
290292
&Pat::Bind { subpat, .. } => {
291293
if let Some(subpat) = subpat {
292294
f(subpat);
@@ -322,6 +324,143 @@ impl Body {
322324
None => true,
323325
}
324326
}
327+
328+
pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
329+
let expr = &self[expr_id];
330+
match expr {
331+
Expr::Continue { .. }
332+
| Expr::Const(_)
333+
| Expr::Missing
334+
| Expr::Path(_)
335+
| Expr::OffsetOf(_)
336+
| Expr::Literal(_)
337+
| Expr::Underscore => {}
338+
Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
339+
AsmOperand::In { expr, .. }
340+
| AsmOperand::Out { expr: Some(expr), .. }
341+
| AsmOperand::InOut { expr, .. } => f(*expr),
342+
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
343+
f(*in_expr);
344+
if let Some(out_expr) = out_expr {
345+
f(*out_expr);
346+
}
347+
}
348+
AsmOperand::Out { expr: None, .. }
349+
| AsmOperand::Const(_)
350+
| AsmOperand::Label(_)
351+
| AsmOperand::Sym(_) => (),
352+
}),
353+
Expr::If { condition, then_branch, else_branch } => {
354+
f(*condition);
355+
f(*then_branch);
356+
if let &Some(else_branch) = else_branch {
357+
f(else_branch);
358+
}
359+
}
360+
Expr::Let { expr, .. } => {
361+
f(*expr);
362+
}
363+
Expr::Block { statements, tail, .. }
364+
| Expr::Unsafe { statements, tail, .. }
365+
| Expr::Async { statements, tail, .. } => {
366+
for stmt in statements.iter() {
367+
match stmt {
368+
Statement::Let { initializer, else_branch, pat, .. } => {
369+
if let &Some(expr) = initializer {
370+
f(expr);
371+
}
372+
if let &Some(expr) = else_branch {
373+
f(expr);
374+
}
375+
walk_exprs_in_pat(self, *pat, &mut f);
376+
}
377+
Statement::Expr { expr: expression, .. } => f(*expression),
378+
Statement::Item => (),
379+
}
380+
}
381+
if let &Some(expr) = tail {
382+
f(expr);
383+
}
384+
}
385+
Expr::Loop { body, .. } => f(*body),
386+
Expr::Call { callee, args, .. } => {
387+
f(*callee);
388+
args.iter().copied().for_each(f);
389+
}
390+
Expr::MethodCall { receiver, args, .. } => {
391+
f(*receiver);
392+
args.iter().copied().for_each(f);
393+
}
394+
Expr::Match { expr, arms } => {
395+
f(*expr);
396+
arms.iter().map(|arm| arm.expr).for_each(f);
397+
}
398+
Expr::Break { expr, .. }
399+
| Expr::Return { expr }
400+
| Expr::Yield { expr }
401+
| Expr::Yeet { expr } => {
402+
if let &Some(expr) = expr {
403+
f(expr);
404+
}
405+
}
406+
Expr::Become { expr } => f(*expr),
407+
Expr::RecordLit { fields, spread, .. } => {
408+
for field in fields.iter() {
409+
f(field.expr);
410+
}
411+
if let &Some(expr) = spread {
412+
f(expr);
413+
}
414+
}
415+
Expr::Closure { body, .. } => {
416+
f(*body);
417+
}
418+
Expr::BinaryOp { lhs, rhs, .. } => {
419+
f(*lhs);
420+
f(*rhs);
421+
}
422+
Expr::Range { lhs, rhs, .. } => {
423+
if let &Some(lhs) = rhs {
424+
f(lhs);
425+
}
426+
if let &Some(rhs) = lhs {
427+
f(rhs);
428+
}
429+
}
430+
Expr::Index { base, index, .. } => {
431+
f(*base);
432+
f(*index);
433+
}
434+
Expr::Field { expr, .. }
435+
| Expr::Await { expr }
436+
| Expr::Cast { expr, .. }
437+
| Expr::Ref { expr, .. }
438+
| Expr::UnaryOp { expr, .. }
439+
| Expr::Box { expr } => {
440+
f(*expr);
441+
}
442+
Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
443+
Expr::Array(a) => match a {
444+
Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
445+
Array::Repeat { initializer, repeat } => {
446+
f(*initializer);
447+
f(*repeat)
448+
}
449+
},
450+
&Expr::Assignment { target, value } => {
451+
walk_exprs_in_pat(self, target, &mut f);
452+
f(value);
453+
}
454+
}
455+
456+
fn walk_exprs_in_pat(this: &Body, pat_id: PatId, f: &mut impl FnMut(ExprId)) {
457+
this.walk_pats(pat_id, &mut |pat| {
458+
if let Pat::Expr(expr) | Pat::ConstBlock(expr) = this[pat] {
459+
f(expr);
460+
}
461+
});
462+
}
463+
}
325464
}
326465

327466
impl Default for Body {

0 commit comments

Comments
 (0)