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