Skip to content

Commit c286786

Browse files
authored
Merge pull request #18254 from ChayimFriedman2/fix-mut
fix: Nail destructuring assignment once and for all
2 parents b35d93c + 0beec90 commit c286786

29 files changed

+1171
-860
lines changed

crates/hir-def/src/body.rs

+167-11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::ops::{Deref, Index};
1010

1111
use base_db::CrateId;
1212
use cfg::{CfgExpr, CfgOptions};
13+
use either::Either;
1314
use hir_expand::{name::Name, ExpandError, InFile};
1415
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
1516
use rustc_hash::FxHashMap;
@@ -22,7 +23,8 @@ use crate::{
2223
db::DefDatabase,
2324
expander::Expander,
2425
hir::{
25-
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
26+
dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label,
27+
LabelId, Pat, PatId, RecordFieldPat, Statement,
2628
},
2729
item_tree::AttrOwner,
2830
nameres::DefMap,
@@ -67,9 +69,12 @@ pub type LabelSource = InFile<LabelPtr>;
6769
pub type FieldPtr = AstPtr<ast::RecordExprField>;
6870
pub type FieldSource = InFile<FieldPtr>;
6971

70-
pub type PatFieldPtr = AstPtr<ast::RecordPatField>;
72+
pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>;
7173
pub type PatFieldSource = InFile<PatFieldPtr>;
7274

75+
pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
76+
pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
77+
7378
/// An item body together with the mapping from syntax nodes to HIR expression
7479
/// IDs. This is needed to go from e.g. a position in a file to the HIR
7580
/// expression containing it; but for type inference etc., we want to operate on
@@ -83,11 +88,13 @@ pub type PatFieldSource = InFile<PatFieldPtr>;
8388
/// this properly for macros.
8489
#[derive(Default, Debug, Eq, PartialEq)]
8590
pub struct BodySourceMap {
86-
expr_map: FxHashMap<ExprSource, ExprId>,
91+
// AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
92+
// to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
93+
expr_map: FxHashMap<ExprSource, ExprOrPatId>,
8794
expr_map_back: ArenaMap<ExprId, ExprSource>,
8895

8996
pat_map: FxHashMap<PatSource, PatId>,
90-
pat_map_back: ArenaMap<PatId, PatSource>,
97+
pat_map_back: ArenaMap<PatId, ExprOrPatSource>,
9198

9299
label_map: FxHashMap<LabelSource, LabelId>,
93100
label_map_back: ArenaMap<LabelId, LabelSource>,
@@ -286,7 +293,8 @@ impl Body {
286293
| Pat::Path(..)
287294
| Pat::ConstBlock(..)
288295
| Pat::Wild
289-
| Pat::Missing => {}
296+
| Pat::Missing
297+
| Pat::Expr(_) => {}
290298
&Pat::Bind { subpat, .. } => {
291299
if let Some(subpat) = subpat {
292300
f(subpat);
@@ -322,6 +330,143 @@ impl Body {
322330
None => true,
323331
}
324332
}
333+
334+
pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
335+
let expr = &self[expr_id];
336+
match expr {
337+
Expr::Continue { .. }
338+
| Expr::Const(_)
339+
| Expr::Missing
340+
| Expr::Path(_)
341+
| Expr::OffsetOf(_)
342+
| Expr::Literal(_)
343+
| Expr::Underscore => {}
344+
Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
345+
AsmOperand::In { expr, .. }
346+
| AsmOperand::Out { expr: Some(expr), .. }
347+
| AsmOperand::InOut { expr, .. } => f(*expr),
348+
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
349+
f(*in_expr);
350+
if let Some(out_expr) = out_expr {
351+
f(*out_expr);
352+
}
353+
}
354+
AsmOperand::Out { expr: None, .. }
355+
| AsmOperand::Const(_)
356+
| AsmOperand::Label(_)
357+
| AsmOperand::Sym(_) => (),
358+
}),
359+
Expr::If { condition, then_branch, else_branch } => {
360+
f(*condition);
361+
f(*then_branch);
362+
if let &Some(else_branch) = else_branch {
363+
f(else_branch);
364+
}
365+
}
366+
Expr::Let { expr, .. } => {
367+
f(*expr);
368+
}
369+
Expr::Block { statements, tail, .. }
370+
| Expr::Unsafe { statements, tail, .. }
371+
| Expr::Async { statements, tail, .. } => {
372+
for stmt in statements.iter() {
373+
match stmt {
374+
Statement::Let { initializer, else_branch, pat, .. } => {
375+
if let &Some(expr) = initializer {
376+
f(expr);
377+
}
378+
if let &Some(expr) = else_branch {
379+
f(expr);
380+
}
381+
self.walk_exprs_in_pat(*pat, &mut f);
382+
}
383+
Statement::Expr { expr: expression, .. } => f(*expression),
384+
Statement::Item => (),
385+
}
386+
}
387+
if let &Some(expr) = tail {
388+
f(expr);
389+
}
390+
}
391+
Expr::Loop { body, .. } => f(*body),
392+
Expr::Call { callee, args, .. } => {
393+
f(*callee);
394+
args.iter().copied().for_each(f);
395+
}
396+
Expr::MethodCall { receiver, args, .. } => {
397+
f(*receiver);
398+
args.iter().copied().for_each(f);
399+
}
400+
Expr::Match { expr, arms } => {
401+
f(*expr);
402+
arms.iter().map(|arm| arm.expr).for_each(f);
403+
}
404+
Expr::Break { expr, .. }
405+
| Expr::Return { expr }
406+
| Expr::Yield { expr }
407+
| Expr::Yeet { expr } => {
408+
if let &Some(expr) = expr {
409+
f(expr);
410+
}
411+
}
412+
Expr::Become { expr } => f(*expr),
413+
Expr::RecordLit { fields, spread, .. } => {
414+
for field in fields.iter() {
415+
f(field.expr);
416+
}
417+
if let &Some(expr) = spread {
418+
f(expr);
419+
}
420+
}
421+
Expr::Closure { body, .. } => {
422+
f(*body);
423+
}
424+
Expr::BinaryOp { lhs, rhs, .. } => {
425+
f(*lhs);
426+
f(*rhs);
427+
}
428+
Expr::Range { lhs, rhs, .. } => {
429+
if let &Some(lhs) = rhs {
430+
f(lhs);
431+
}
432+
if let &Some(rhs) = lhs {
433+
f(rhs);
434+
}
435+
}
436+
Expr::Index { base, index, .. } => {
437+
f(*base);
438+
f(*index);
439+
}
440+
Expr::Field { expr, .. }
441+
| Expr::Await { expr }
442+
| Expr::Cast { expr, .. }
443+
| Expr::Ref { expr, .. }
444+
| Expr::UnaryOp { expr, .. }
445+
| Expr::Box { expr } => {
446+
f(*expr);
447+
}
448+
Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
449+
Expr::Array(a) => match a {
450+
Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
451+
Array::Repeat { initializer, repeat } => {
452+
f(*initializer);
453+
f(*repeat)
454+
}
455+
},
456+
&Expr::Assignment { target, value } => {
457+
self.walk_exprs_in_pat(target, &mut f);
458+
f(value);
459+
}
460+
}
461+
}
462+
463+
pub fn walk_exprs_in_pat(&self, pat_id: PatId, f: &mut impl FnMut(ExprId)) {
464+
self.walk_pats(pat_id, &mut |pat| {
465+
if let Pat::Expr(expr) | Pat::ConstBlock(expr) = self[pat] {
466+
f(expr);
467+
}
468+
});
469+
}
325470
}
326471

327472
impl Default for Body {
@@ -375,11 +520,18 @@ impl Index<BindingId> for Body {
375520
// FIXME: Change `node_` prefix to something more reasonable.
376521
// Perhaps `expr_syntax` and `expr_id`?
377522
impl BodySourceMap {
523+
pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
524+
match id {
525+
ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)),
526+
ExprOrPatId::PatId(id) => self.pat_syntax(id),
527+
}
528+
}
529+
378530
pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
379531
self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
380532
}
381533

382-
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
534+
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprOrPatId> {
383535
let src = node.map(AstPtr::new);
384536
self.expr_map.get(&src).cloned()
385537
}
@@ -395,7 +547,7 @@ impl BodySourceMap {
395547
self.expansions.iter().map(|(&a, &b)| (a, b))
396548
}
397549

398-
pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
550+
pub fn pat_syntax(&self, pat: PatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
399551
self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
400552
}
401553

@@ -428,7 +580,7 @@ impl BodySourceMap {
428580
self.pat_field_map_back[&pat]
429581
}
430582

431-
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> {
583+
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprOrPatId> {
432584
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
433585
self.expr_map.get(&src).copied()
434586
}
@@ -444,16 +596,20 @@ impl BodySourceMap {
444596
node: InFile<&ast::FormatArgsExpr>,
445597
) -> Option<&[(syntax::TextRange, Name)]> {
446598
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
447-
self.template_map.as_ref()?.0.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref)
599+
self.template_map
600+
.as_ref()?
601+
.0
602+
.get(&self.expr_map.get(&src)?.as_expr()?)
603+
.map(std::ops::Deref::deref)
448604
}
449605

450606
pub fn asm_template_args(
451607
&self,
452608
node: InFile<&ast::AsmExpr>,
453609
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
454610
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
455-
let expr = self.expr_map.get(&src)?;
456-
Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref))
611+
let expr = self.expr_map.get(&src)?.as_expr()?;
612+
Some(expr).zip(self.template_map.as_ref()?.1.get(&expr).map(std::ops::Deref::deref))
457613
}
458614

459615
/// Get a reference to the body source map's diagnostics.

0 commit comments

Comments
 (0)