Skip to main content

hir_ty/mir/
borrowck.rs

1//! MIR borrow checker, which is used in diagnostics like `unused_mut`
2
3// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
4// if needed for implementing a proper borrow checker.
5
6use std::iter;
7
8use either::Either;
9use hir_def::HasModule;
10use la_arena::ArenaMap;
11use rustc_hash::FxHashMap;
12use salsa::Update;
13use stdx::never;
14
15use crate::{
16    InferBodyId,
17    closure_analysis::ProjectionKind as HirProjectionKind,
18    db::{HirDatabase, InternedClosureId},
19    display::DisplayTarget,
20    mir::{OperandKind, PlaceTy},
21    next_solver::{
22        DbInterner, ParamEnv, StoredTy, TypingMode,
23        infer::{DbInternerInferExt, InferCtxt},
24    },
25};
26
27use super::{
28    BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, MutBorrowKind, Operand,
29    Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind,
30};
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33/// Stores spans which implies that the local should be mutable.
34pub enum MutabilityReason {
35    Mut { spans: Vec<MirSpan> },
36    Not,
37    Unused,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct MovedOutOfRef {
42    pub ty: StoredTy,
43    pub span: MirSpan,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct PartiallyMoved {
48    pub ty: StoredTy,
49    pub span: MirSpan,
50    pub local: LocalId,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct BorrowRegion {
55    pub local: LocalId,
56    pub kind: BorrowKind,
57    pub places: Vec<MirSpan>,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Update)]
61pub struct BorrowckResult<'db> {
62    owner: Either<InferBodyId<'db>, InternedClosureId<'db>>,
63    pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
64    pub moved_out_of_ref: Vec<MovedOutOfRef>,
65    pub partially_moved: Vec<PartiallyMoved>,
66    pub borrow_regions: Vec<BorrowRegion>,
67}
68
69impl<'db> BorrowckResult<'db> {
70    pub fn mir_body(&self, db: &'db dyn HirDatabase) -> &'db MirBody<'db> {
71        match self.owner {
72            Either::Left(it) => db.mir_body(it).unwrap(),
73            Either::Right(it) => db.mir_body_for_closure(it).unwrap(),
74        }
75    }
76}
77
78fn all_mir_bodies<'db>(
79    db: &'db dyn HirDatabase,
80    def: InferBodyId<'db>,
81    mut cb: impl FnMut(
82        &'db MirBody<'db>,
83        Either<InferBodyId<'db>, InternedClosureId<'db>>,
84    ) -> BorrowckResult<'db>,
85    mut merge_from_closures: impl FnMut(
86        (&mut BorrowckResult<'db>, &'db MirBody<'db>),
87        (&BorrowckResult<'db>, &'db MirBody<'db>),
88    ),
89) -> Result<Box<[BorrowckResult<'db>]>, MirLowerError<'db>> {
90    fn for_closure<'db>(
91        db: &'db dyn HirDatabase,
92        c: InternedClosureId<'db>,
93        results: &mut Vec<(BorrowckResult<'db>, &'db MirBody<'db>)>,
94        cb: &mut impl FnMut(
95            &'db MirBody<'db>,
96            Either<InferBodyId<'db>, InternedClosureId<'db>>,
97        ) -> BorrowckResult<'db>,
98        merge_from_closures: &mut impl FnMut(
99            (&mut BorrowckResult<'db>, &'db MirBody<'db>),
100            (&BorrowckResult<'db>, &'db MirBody<'db>),
101        ),
102    ) -> Result<(), MirLowerError<'db>> {
103        match db.mir_body_for_closure(c) {
104            Ok(body) => {
105                let parent_index = results.len();
106                results.push((cb(body, Either::Right(c)), body));
107                body.closures
108                    .iter()
109                    .try_for_each(|&it| for_closure(db, it, results, cb, merge_from_closures))?;
110                merge(results, merge_from_closures, parent_index);
111                Ok(())
112            }
113            Err(e) => Err(e.clone()),
114        }
115    }
116
117    fn merge<'db>(
118        results: &mut [(BorrowckResult<'db>, &'db MirBody<'db>)],
119        merge: &mut impl FnMut(
120            (&mut BorrowckResult<'db>, &'db MirBody<'db>),
121            (&BorrowckResult<'db>, &'db MirBody<'db>),
122        ),
123        parent_index: usize,
124    ) {
125        let (parent_and_before, children) = results.split_at_mut(parent_index + 1);
126        let (parent, parent_mir_body) = &mut parent_and_before[parent_and_before.len() - 1];
127        children.iter().for_each(|(child, child_mir_body)| {
128            merge((parent, parent_mir_body), (child, child_mir_body))
129        });
130    }
131
132    let mut results = Vec::new();
133    match db.mir_body(def) {
134        Ok(body) => {
135            results.push((cb(body, Either::Left(def)), body));
136            body.closures.iter().try_for_each(|&it| {
137                for_closure(db, it, &mut results, &mut cb, &mut merge_from_closures)
138            })?;
139            merge(&mut results, &mut merge_from_closures, 0);
140            Ok(results.into_iter().map(|(it, _)| it).collect())
141        }
142        Err(e) => Err(e.clone()),
143    }
144}
145
146impl<'db> InferBodyId<'db> {
147    pub fn borrowck(
148        self,
149        db: &'db dyn HirDatabase,
150    ) -> Result<&'db [BorrowckResult<'db>], MirLowerError<'db>> {
151        return borrowck_query(db, self).map_err(|e| e.clone());
152
153        #[salsa::tracked(returns(as_deref), lru = 2024)]
154        fn borrowck_query<'db>(
155            db: &'db dyn HirDatabase,
156            def: InferBodyId<'db>,
157        ) -> Result<Box<[BorrowckResult<'db>]>, MirLowerError<'db>> {
158            let _p = tracing::info_span!("InferBodyId::borrowck").entered();
159            let module = def.module(db);
160            let interner = DbInterner::new_with(db, module.krate(db));
161            let env = db.trait_environment(def.generic_def(db));
162            // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`.
163            let typing_mode = TypingMode::borrowck(interner, def.into());
164            all_mir_bodies(
165                db,
166                def,
167                |body, owner| {
168                    // FIXME(next-solver): Opaques.
169                    let infcx = interner.infer_ctxt().build(typing_mode);
170                    BorrowckResult {
171                        owner,
172                        mutability_of_locals: mutability_of_locals(&infcx, env, body),
173                        moved_out_of_ref: moved_out_of_ref(&infcx, env, body),
174                        partially_moved: partially_moved(&infcx, env, body),
175                        borrow_regions: borrow_regions(db, body),
176                    }
177                },
178                |(parent, parent_mir_body), (child, child_mir_body)| {
179                    for (upvar, child_locals) in &child_mir_body.upvar_locals {
180                        let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else {
181                            continue;
182                        };
183                        for (child_local, capture_place) in child_locals {
184                            if !capture_place
185                                .projections
186                                .iter()
187                                .any(|proj| matches!(proj.kind, HirProjectionKind::Deref))
188                            {
189                                let parent_mol = &mut parent.mutability_of_locals[parent_local];
190                                match (&*parent_mol, &child.mutability_of_locals[*child_local]) {
191                                    (MutabilityReason::Mut { .. }, _) => {}
192                                    (_, MutabilityReason::Mut { .. }) => {
193                                        // FIXME: Fix the child spans.
194                                        *parent_mol = MutabilityReason::Mut { spans: Vec::new() }
195                                    }
196                                    (MutabilityReason::Not, _) => {}
197                                    (_, MutabilityReason::Not) => {
198                                        *parent_mol = MutabilityReason::Not
199                                    }
200                                    (MutabilityReason::Unused, MutabilityReason::Unused) => {}
201                                }
202                            }
203                        }
204                    }
205                },
206            )
207        }
208    }
209}
210
211fn moved_out_of_ref<'db>(
212    infcx: &InferCtxt<'db>,
213    env: ParamEnv<'db>,
214    body: &MirBody<'db>,
215) -> Vec<MovedOutOfRef> {
216    let db = infcx.interner.db;
217    let mut result = vec![];
218    let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind {
219        OperandKind::Copy(p) | OperandKind::Move(p) => {
220            let mut ty = PlaceTy::from_ty(body.locals[p.local].ty.as_ref());
221            let mut is_dereference_of_ref = false;
222            for proj in p.projection.lookup() {
223                if *proj == ProjectionElem::Deref && ty.ty.as_reference().is_some() {
224                    is_dereference_of_ref = true;
225                }
226                ty = ty.projection_ty(infcx, proj, env);
227            }
228            if is_dereference_of_ref
229                && !infcx.type_is_copy_modulo_regions(env, ty.ty)
230                && !ty.ty.references_non_lt_error()
231            {
232                result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.ty.store() });
233            }
234        }
235        OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
236    };
237    for (_, block) in body.basic_blocks.iter() {
238        db.unwind_if_revision_cancelled();
239        for statement in &block.statements {
240            match &statement.kind {
241                StatementKind::Assign(_, r) => match r {
242                    Rvalue::ShallowInitBoxWithAlloc(_) => (),
243                    Rvalue::ShallowInitBox(o, _)
244                    | Rvalue::UnaryOp(_, o)
245                    | Rvalue::Cast(_, o, _)
246                    | Rvalue::Repeat(o, _)
247                    | Rvalue::Use(o) => for_operand(o, statement.span),
248                    Rvalue::CopyForDeref(_)
249                    | Rvalue::Discriminant(_)
250                    | Rvalue::Len(_)
251                    | Rvalue::Ref(_, _) => (),
252                    Rvalue::CheckedBinaryOp(_, o1, o2) => {
253                        for_operand(o1, statement.span);
254                        for_operand(o2, statement.span);
255                    }
256                    Rvalue::Aggregate(_, ops) => {
257                        for op in ops.iter() {
258                            for_operand(op, statement.span);
259                        }
260                    }
261                    Rvalue::ThreadLocalRef(n)
262                    | Rvalue::AddressOf(n)
263                    | Rvalue::BinaryOp(n)
264                    | Rvalue::NullaryOp(n) => match *n {},
265                },
266                StatementKind::FakeRead(_)
267                | StatementKind::Deinit(_)
268                | StatementKind::StorageLive(_)
269                | StatementKind::StorageDead(_)
270                | StatementKind::Nop => (),
271            }
272        }
273        match &block.terminator {
274            Some(terminator) => match &terminator.kind {
275                TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
276                TerminatorKind::FalseEdge { .. }
277                | TerminatorKind::FalseUnwind { .. }
278                | TerminatorKind::Goto { .. }
279                | TerminatorKind::UnwindResume
280                | TerminatorKind::CoroutineDrop
281                | TerminatorKind::Abort
282                | TerminatorKind::Return
283                | TerminatorKind::Unreachable
284                | TerminatorKind::Drop { .. } => (),
285                TerminatorKind::DropAndReplace { value, .. } => {
286                    for_operand(value, terminator.span);
287                }
288                TerminatorKind::Call { func, args, .. } => {
289                    for_operand(func, terminator.span);
290                    args.iter().for_each(|it| for_operand(it, terminator.span));
291                }
292                TerminatorKind::Assert { cond, .. } => {
293                    for_operand(cond, terminator.span);
294                }
295                TerminatorKind::Yield { value, .. } => {
296                    for_operand(value, terminator.span);
297                }
298            },
299            None => (),
300        }
301    }
302    result.shrink_to_fit();
303    result
304}
305
306fn partially_moved<'db>(
307    infcx: &InferCtxt<'db>,
308    env: ParamEnv<'db>,
309    body: &MirBody<'db>,
310) -> Vec<PartiallyMoved> {
311    let db = infcx.interner.db;
312    let mut result = vec![];
313    let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind {
314        OperandKind::Copy(p) | OperandKind::Move(p) => {
315            let ty = p.as_ref().ty(body, infcx, env).ty;
316            if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() {
317                result.push(PartiallyMoved { span, ty: ty.store(), local: p.local });
318            }
319        }
320        OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
321    };
322    for (_, block) in body.basic_blocks.iter() {
323        db.unwind_if_revision_cancelled();
324        for statement in &block.statements {
325            match &statement.kind {
326                StatementKind::Assign(_, r) => match r {
327                    Rvalue::ShallowInitBoxWithAlloc(_) => (),
328                    Rvalue::ShallowInitBox(o, _)
329                    | Rvalue::UnaryOp(_, o)
330                    | Rvalue::Cast(_, o, _)
331                    | Rvalue::Repeat(o, _)
332                    | Rvalue::Use(o) => for_operand(o, statement.span),
333                    Rvalue::CopyForDeref(_)
334                    | Rvalue::Discriminant(_)
335                    | Rvalue::Len(_)
336                    | Rvalue::Ref(_, _) => (),
337                    Rvalue::CheckedBinaryOp(_, o1, o2) => {
338                        for_operand(o1, statement.span);
339                        for_operand(o2, statement.span);
340                    }
341                    Rvalue::Aggregate(_, ops) => {
342                        for op in ops.iter() {
343                            for_operand(op, statement.span);
344                        }
345                    }
346                    Rvalue::ThreadLocalRef(n)
347                    | Rvalue::AddressOf(n)
348                    | Rvalue::BinaryOp(n)
349                    | Rvalue::NullaryOp(n) => match *n {},
350                },
351                StatementKind::FakeRead(_)
352                | StatementKind::Deinit(_)
353                | StatementKind::StorageLive(_)
354                | StatementKind::StorageDead(_)
355                | StatementKind::Nop => (),
356            }
357        }
358        match &block.terminator {
359            Some(terminator) => match &terminator.kind {
360                TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
361                TerminatorKind::FalseEdge { .. }
362                | TerminatorKind::FalseUnwind { .. }
363                | TerminatorKind::Goto { .. }
364                | TerminatorKind::UnwindResume
365                | TerminatorKind::CoroutineDrop
366                | TerminatorKind::Abort
367                | TerminatorKind::Return
368                | TerminatorKind::Unreachable
369                | TerminatorKind::Drop { .. } => (),
370                TerminatorKind::DropAndReplace { value, .. } => {
371                    for_operand(value, terminator.span);
372                }
373                TerminatorKind::Call { func, args, .. } => {
374                    for_operand(func, terminator.span);
375                    args.iter().for_each(|it| for_operand(it, terminator.span));
376                }
377                TerminatorKind::Assert { cond, .. } => {
378                    for_operand(cond, terminator.span);
379                }
380                TerminatorKind::Yield { value, .. } => {
381                    for_operand(value, terminator.span);
382                }
383            },
384            None => (),
385        }
386    }
387    result.shrink_to_fit();
388    result
389}
390
391fn borrow_regions<'db>(db: &'db dyn HirDatabase, body: &MirBody<'db>) -> Vec<BorrowRegion> {
392    let mut borrows = FxHashMap::default();
393    for (_, block) in body.basic_blocks.iter() {
394        db.unwind_if_revision_cancelled();
395        for statement in &block.statements {
396            if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind {
397                borrows
398                    .entry(p.local)
399                    .and_modify(|it: &mut BorrowRegion| {
400                        it.places.push(statement.span);
401                    })
402                    .or_insert_with(|| BorrowRegion {
403                        local: p.local,
404                        kind: *kind,
405                        places: vec![statement.span],
406                    });
407            }
408        }
409        match &block.terminator {
410            Some(terminator) => match &terminator.kind {
411                TerminatorKind::FalseEdge { .. }
412                | TerminatorKind::FalseUnwind { .. }
413                | TerminatorKind::Goto { .. }
414                | TerminatorKind::UnwindResume
415                | TerminatorKind::CoroutineDrop
416                | TerminatorKind::Abort
417                | TerminatorKind::Return
418                | TerminatorKind::Unreachable
419                | TerminatorKind::Drop { .. } => (),
420                TerminatorKind::DropAndReplace { .. } => {}
421                TerminatorKind::Call { .. } => {}
422                _ => (),
423            },
424            None => (),
425        }
426    }
427
428    borrows.into_values().collect()
429}
430
431#[derive(Debug, Clone, Copy, PartialEq, Eq)]
432enum ProjectionCase {
433    /// Projection is a local
434    Direct,
435    /// Projection is some field or slice of a local
436    DirectPart,
437    /// Projection is deref of something
438    Indirect,
439}
440
441fn place_case<'db>(
442    infcx: &InferCtxt<'db>,
443    env: ParamEnv<'db>,
444    body: &MirBody<'db>,
445    lvalue: &Place,
446) -> ProjectionCase {
447    let mut is_part_of = false;
448    let mut ty = PlaceTy::from_ty(body.locals[lvalue.local].ty.as_ref());
449    for proj in lvalue.projection.lookup().iter() {
450        match proj {
451            ProjectionElem::Deref if ty.ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
452            ProjectionElem::Deref // It's direct in case of `Box<T>`
453            | ProjectionElem::ConstantIndex { .. }
454            | ProjectionElem::Subslice { .. }
455            | ProjectionElem::Field(_)
456            | ProjectionElem::Index(_) => {
457                is_part_of = true;
458            }
459            ProjectionElem::Downcast(_) => (),
460        }
461        ty = ty.projection_ty(infcx, proj, env);
462    }
463    if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct }
464}
465
466/// Returns a map from basic blocks to the set of locals that might be ever initialized before
467/// the start of the block. Only `StorageDead` can remove something from this map, and we ignore
468/// `Uninit` and `drop` and similar after initialization.
469fn ever_initialized_map(
470    db: &dyn HirDatabase,
471    body: &MirBody<'_>,
472) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
473    let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
474        body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect();
475    fn dfs(
476        db: &dyn HirDatabase,
477        body: &MirBody<'_>,
478        l: LocalId,
479        stack: &mut Vec<BasicBlockId>,
480        result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
481    ) {
482        while let Some(b) = stack.pop() {
483            let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
484            let block = &body.basic_blocks[b];
485            for statement in &block.statements {
486                match &statement.kind {
487                    StatementKind::Assign(p, _) => {
488                        if p.projection.is_empty() && p.local == l {
489                            is_ever_initialized = true;
490                        }
491                    }
492                    StatementKind::StorageDead(p) => {
493                        if *p == l {
494                            is_ever_initialized = false;
495                        }
496                    }
497                    StatementKind::Deinit(_)
498                    | StatementKind::FakeRead(_)
499                    | StatementKind::Nop
500                    | StatementKind::StorageLive(_) => (),
501                }
502            }
503            let Some(terminator) = &block.terminator else {
504                never!(
505                    "Terminator should be none only in construction.\nThe body:\n{}",
506                    body.pretty_print(db, DisplayTarget::from_crate(db, body.owner.krate(db)))
507                );
508                return;
509            };
510            let mut process = |target, is_ever_initialized| {
511                if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
512                    result[target].insert(l, is_ever_initialized);
513                    stack.push(target);
514                }
515            };
516            match &terminator.kind {
517                TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
518                TerminatorKind::SwitchInt { targets, .. } => {
519                    targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
520                }
521                TerminatorKind::UnwindResume
522                | TerminatorKind::Abort
523                | TerminatorKind::Return
524                | TerminatorKind::Unreachable => (),
525                TerminatorKind::Call { target, cleanup, destination, .. } => {
526                    if destination.projection.is_empty() && destination.local == l {
527                        is_ever_initialized = true;
528                    }
529                    target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
530                }
531                TerminatorKind::Drop { target, unwind, place: _ } => {
532                    iter::once(target)
533                        .chain(unwind)
534                        .for_each(|&it| process(it, is_ever_initialized));
535                }
536                TerminatorKind::DropAndReplace { .. }
537                | TerminatorKind::Assert { .. }
538                | TerminatorKind::Yield { .. }
539                | TerminatorKind::CoroutineDrop
540                | TerminatorKind::FalseEdge { .. }
541                | TerminatorKind::FalseUnwind { .. } => {
542                    never!("We don't emit these MIR terminators yet");
543                }
544            }
545        }
546    }
547    let mut stack = Vec::new();
548    for &l in &body.param_locals {
549        result[body.start_block].insert(l, true);
550        stack.clear();
551        stack.push(body.start_block);
552        dfs(db, body, l, &mut stack, &mut result);
553    }
554    for l in body.locals.iter().map(|it| it.0) {
555        db.unwind_if_revision_cancelled();
556        if !result[body.start_block].contains_idx(l) {
557            result[body.start_block].insert(l, false);
558            stack.clear();
559            stack.push(body.start_block);
560            dfs(db, body, l, &mut stack, &mut result);
561        }
562    }
563    result
564}
565
566fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) {
567    match &mut result[local] {
568        MutabilityReason::Mut { spans } => spans.push(span),
569        it @ (MutabilityReason::Not | MutabilityReason::Unused) => {
570            *it = MutabilityReason::Mut { spans: vec![span] }
571        }
572    };
573}
574
575fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) {
576    if let it @ MutabilityReason::Unused = &mut result[local] {
577        *it = MutabilityReason::Not;
578    };
579}
580
581fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) {
582    if let OperandKind::Copy(p) | OperandKind::Move(p) = &arg.kind {
583        record_usage(p.local, result);
584    }
585}
586
587fn mutability_of_locals<'db>(
588    infcx: &InferCtxt<'db>,
589    env: ParamEnv<'db>,
590    body: &MirBody<'db>,
591) -> ArenaMap<LocalId, MutabilityReason> {
592    let db = infcx.interner.db;
593    let mut result: ArenaMap<LocalId, MutabilityReason> =
594        body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect();
595
596    let ever_init_maps = ever_initialized_map(db, body);
597    for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
598        let block = &body.basic_blocks[block_id];
599        for statement in &block.statements {
600            match &statement.kind {
601                StatementKind::Assign(place, value) => {
602                    match place_case(infcx, env, body, place) {
603                        ProjectionCase::Direct => {
604                            if ever_init_map.get(place.local).copied().unwrap_or_default() {
605                                push_mut_span(place.local, statement.span, &mut result);
606                            } else {
607                                ever_init_map.insert(place.local, true);
608                            }
609                        }
610                        ProjectionCase::DirectPart => {
611                            // Partial initialization is not supported, so it is definitely `mut`
612                            push_mut_span(place.local, statement.span, &mut result);
613                        }
614                        ProjectionCase::Indirect => {
615                            record_usage(place.local, &mut result);
616                        }
617                    }
618                    match value {
619                        Rvalue::CopyForDeref(p)
620                        | Rvalue::Discriminant(p)
621                        | Rvalue::Len(p)
622                        | Rvalue::Ref(_, p) => {
623                            record_usage(p.local, &mut result);
624                        }
625                        Rvalue::Use(o)
626                        | Rvalue::Repeat(o, _)
627                        | Rvalue::Cast(_, o, _)
628                        | Rvalue::UnaryOp(_, o) => record_usage_for_operand(o, &mut result),
629                        Rvalue::CheckedBinaryOp(_, o1, o2) => {
630                            for o in [o1, o2] {
631                                record_usage_for_operand(o, &mut result);
632                            }
633                        }
634                        Rvalue::Aggregate(_, args) => {
635                            for arg in args.iter() {
636                                record_usage_for_operand(arg, &mut result);
637                            }
638                        }
639                        Rvalue::ShallowInitBox(_, _) | Rvalue::ShallowInitBoxWithAlloc(_) => (),
640                        Rvalue::ThreadLocalRef(n)
641                        | Rvalue::AddressOf(n)
642                        | Rvalue::BinaryOp(n)
643                        | Rvalue::NullaryOp(n) => match *n {},
644                    }
645                    if let Rvalue::Ref(
646                        BorrowKind::Mut {
647                            kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
648                        },
649                        p,
650                    ) = value
651                        && place_case(infcx, env, body, p) != ProjectionCase::Indirect
652                    {
653                        push_mut_span(p.local, statement.span, &mut result);
654                    }
655                }
656                StatementKind::FakeRead(p) => {
657                    record_usage(p.local, &mut result);
658                }
659                StatementKind::StorageDead(p) => {
660                    ever_init_map.insert(*p, false);
661                }
662                StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::Nop => (),
663            }
664        }
665        let Some(terminator) = &block.terminator else {
666            never!("Terminator should be none only in construction");
667            continue;
668        };
669        match &terminator.kind {
670            TerminatorKind::Goto { .. }
671            | TerminatorKind::UnwindResume
672            | TerminatorKind::Abort
673            | TerminatorKind::Return
674            | TerminatorKind::Unreachable
675            | TerminatorKind::FalseEdge { .. }
676            | TerminatorKind::FalseUnwind { .. }
677            | TerminatorKind::CoroutineDrop
678            | TerminatorKind::Drop { .. }
679            | TerminatorKind::DropAndReplace { .. }
680            | TerminatorKind::Assert { .. }
681            | TerminatorKind::Yield { .. } => (),
682            TerminatorKind::SwitchInt { discr, targets: _ } => {
683                record_usage_for_operand(discr, &mut result);
684            }
685            TerminatorKind::Call { destination, args, func, .. } => {
686                record_usage_for_operand(func, &mut result);
687                for arg in args.iter() {
688                    record_usage_for_operand(arg, &mut result);
689                }
690                if destination.projection.is_empty() {
691                    if ever_init_map.get(destination.local).copied().unwrap_or_default() {
692                        push_mut_span(destination.local, terminator.span, &mut result);
693                    } else {
694                        ever_init_map.insert(destination.local, true);
695                    }
696                }
697            }
698        }
699    }
700    result
701}