Skip to main content

rustc_mir_transform/
liveness.rs

1use rustc_abi::FieldIdx;
2use rustc_attr_ir::find_attr;
3use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry};
4use rustc_hir::def::{CtorKind, DefKind};
5use rustc_hir::def_id::{DefId, LocalDefId};
6use rustc_index::IndexVec;
7use rustc_index::bit_set::DenseBitSet;
8use rustc_lint_defs::builtin::{UNUSED_ASSIGNMENTS, UNUSED_VARIABLES};
9use rustc_middle::mir::visit::{
10    MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor,
11};
12use rustc_middle::mir::*;
13use rustc_middle::ty::print::with_no_trimmed_paths;
14use rustc_middle::ty::{self, Ty, TyCtxt};
15use rustc_mir_dataflow::fmt::DebugWithContext;
16use rustc_mir_dataflow::{Analysis, Backward, ResultsCursor};
17use rustc_span::edit_distance::find_best_match_for_name;
18use rustc_span::symbol::{Symbol, kw, sym};
19use rustc_span::{Span, bug};
20
21use crate::diagnostics;
22
23#[derive(#[automatically_derived]
impl ::core::marker::Copy for AccessKind { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for AccessKind { }
#[automatically_derived]
impl ::core::clone::Clone for AccessKind {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for AccessKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                AccessKind::Param => "Param",
                AccessKind::Assign => "Assign",
                AccessKind::Capture => "Capture",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for AccessKind { }
#[automatically_derived]
impl ::core::cmp::PartialEq for AccessKind {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for AccessKind { }Eq)]
24enum AccessKind {
25    Param,
26    Assign,
27    Capture,
28}
29
30#[derive(#[automatically_derived]
impl ::core::marker::Copy for CaptureKind { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for CaptureKind { }
#[automatically_derived]
impl ::core::clone::Clone for CaptureKind {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<ty::ClosureKind>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for CaptureKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Closure(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Closure", &__self_0),
            Self::Coroutine =>
                ::core::fmt::Formatter::write_str(f, "Coroutine"),
            Self::CoroutineClosure =>
                ::core::fmt::Formatter::write_str(f, "CoroutineClosure"),
            Self::None => ::core::fmt::Formatter::write_str(f, "None"),
        }
    }
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for CaptureKind { }
#[automatically_derived]
impl ::core::cmp::PartialEq for CaptureKind {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::Closure(__self_0), Self::Closure(__arg1_0)) =>
                    __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CaptureKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<ty::ClosureKind>;
    }
}Eq)]
31enum CaptureKind {
32    Closure(ty::ClosureKind),
33    Coroutine,
34    CoroutineClosure,
35    None,
36}
37
38#[derive(#[automatically_derived]
impl ::core::marker::Copy for Access { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Access { }
#[automatically_derived]
impl ::core::clone::Clone for Access {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<AccessKind>;
        let _: ::core::clone::AssertParamIsClone<Location>;
        let _: ::core::clone::AssertParamIsClone<bool>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for Access {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f, "Access",
            "kind", &self.kind, "location", &self.location, "live",
            &self.live, "is_direct", &&self.is_direct)
    }
}Debug)]
39struct Access {
40    /// Describe the current access.
41    kind: AccessKind,
42    /// MIR location where this access happens.
43    location: Location,
44    /// Is the accessed place is live at the current statement?
45    /// When we encounter multiple statements at the same location, we only increase the liveness,
46    /// in order to avoid false positives.
47    live: bool,
48    /// Is this a direct access to the place itself, no projections, or to a field?
49    /// This helps distinguish `x = ...` from `x.field = ...`
50    is_direct: bool,
51}
52
53{}
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
            ::tracing::Level::DEBUG <=
                ::tracing::level_filters::LevelFilter::current() || { false }
    {
    __tracing_attr_span =
        {
            use ::tracing::__macro_support::Callsite as _;
            static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                {
                    static META: ::tracing::Metadata<'static> =
                        {
                            ::tracing_core::metadata::Metadata::new("check_liveness",
                                "rustc_mir_transform::liveness", ::tracing::Level::DEBUG,
                                ::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_mir_transform/src/liveness.rs"),
                                ::tracing_core::__macro_support::Option::Some(53u32),
                                ::tracing_core::__macro_support::Option::Some("rustc_mir_transform::liveness"),
                                ::tracing_core::field::FieldSet::new(&[{
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("def_id")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("def_id");
                                                    NAME.as_str()
                                                }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                ::tracing::metadata::Kind::SPAN)
                        };
                    ::tracing::callsite::DefaultCallsite::new(&META)
                };
            let mut interest = ::tracing::subscriber::Interest::never();
            if ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        { interest = __CALLSITE.interest(); !interest.is_never() }
                    &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest) {
                let meta = __CALLSITE.metadata();
                ::tracing::Span::new(meta,
                    &{
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&def_id)
                                                        as &dyn ::tracing::field::Value))])
                        })
            } else {
                let span =
                    ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                {};
                span
            }
        };
    __tracing_attr_guard = __tracing_attr_span.enter();
}
#[allow(clippy :: redundant_closure_call)]
let x =
    (move ||
                {

                    #[allow(unknown_lints, unreachable_code, clippy ::
                    diverging_sub_expression, clippy :: empty_loop, clippy ::
                    let_unit_value, clippy :: let_with_type_underscore, clippy
                    :: needless_return, clippy :: unreachable)]
                    if false {
                        let __tracing_attr_fake_return: DenseBitSet<FieldIdx> =
                            loop {};
                        return __tracing_attr_fake_return;
                    }
                    {
                        if tcx.is_synthetic_mir(def_id) {
                            return DenseBitSet::new_empty(0);
                        }
                        if tcx.intrinsic(def_id.to_def_id()).is_some() {
                            return DenseBitSet::new_empty(0);
                        }
                        if {
                                    {
                                        'done:
                                            {
                                            for i in
                                                ::rustc_attr_ir::HasAttrs::get_attrs(def_id.to_def_id(),
                                                    &tcx) {
                                                #[allow(unused_imports)]
                                                use ::rustc_attr_ir::AttributeKind::*;
                                                let i: &::rustc_attr_ir::Attribute = i;
                                                match i {
                                                    ::rustc_attr_ir::Attribute::Parsed(Naked(..)) => {
                                                        break 'done Some(());
                                                    }
                                                    ::rustc_attr_ir::Attribute::Unparsed(..) =>
                                                        {}
                                                        #[deny(unreachable_patterns)]
                                                        _ => {}
                                                }
                                            }
                                            None
                                        }
                                    }
                                }.is_some() {
                            return DenseBitSet::new_empty(0);
                        }
                        let parent =
                            tcx.local_parent(tcx.typeck_root_def_id_local(def_id));
                        if let DefKind::Impl { of_trait: true } =
                                    tcx.def_kind(parent) &&
                                {
                                        {
                                            'done:
                                                {
                                                for i in ::rustc_attr_ir::HasAttrs::get_attrs(parent, &tcx)
                                                    {
                                                    #[allow(unused_imports)]
                                                    use ::rustc_attr_ir::AttributeKind::*;
                                                    let i: &::rustc_attr_ir::Attribute = i;
                                                    match i {
                                                        ::rustc_attr_ir::Attribute::Parsed(AutomaticallyDerived) =>
                                                            {
                                                            break 'done Some(());
                                                        }
                                                        ::rustc_attr_ir::Attribute::Unparsed(..) =>
                                                            {}
                                                            #[deny(unreachable_patterns)]
                                                            _ => {}
                                                    }
                                                }
                                                None
                                            }
                                        }
                                    }.is_some() {
                            return DenseBitSet::new_empty(0);
                        }
                        let mut body = &*tcx.mir_promoted(def_id).0.borrow();
                        let mut body_mem;
                        if body.tainted_by_errors.is_some() {
                            return DenseBitSet::new_empty(0);
                        }
                        let mut checked_places = PlaceSet::default();
                        checked_places.insert_locals(&body.local_decls);
                        let (capture_kind, num_captures) =
                            if tcx.is_closure_like(def_id.to_def_id()) {
                                let mut self_ty =
                                    body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
                                let mut self_is_ref = false;
                                if let ty::Ref(_, ty, _) = self_ty.kind() {
                                    self_ty = *ty;
                                    self_is_ref = true;
                                }
                                let (capture_kind, args) =
                                    match self_ty.kind() {
                                        ty::Closure(_, args) => {
                                            (CaptureKind::Closure(args.as_closure().kind()),
                                                ty::UpvarArgs::Closure(args))
                                        }
                                        &ty::Coroutine(_, args) =>
                                            (CaptureKind::Coroutine, ty::UpvarArgs::Coroutine(args)),
                                        &ty::CoroutineClosure(_, args) => {
                                            (CaptureKind::CoroutineClosure,
                                                ty::UpvarArgs::CoroutineClosure(args))
                                        }
                                        _ =>
                                            ::rustc_span::macros::bug_impl(None,
                                                format_args!("expected closure or generator, found {0:?}",
                                                    self_ty), Location::caller()),
                                    };
                                let captures = tcx.closure_captures(def_id);
                                checked_places.insert_captures(tcx, self_is_ref, captures,
                                    args.upvar_tys());
                                if let CaptureKind::Closure(ty::ClosureKind::FnMut) =
                                        capture_kind {
                                    body_mem = body.clone();
                                    for bbdata in body_mem.basic_blocks_mut() {
                                        if let TerminatorKind::Return | TerminatorKind::UnwindResume
                                                = bbdata.terminator().kind {
                                            bbdata.terminator_mut().kind =
                                                TerminatorKind::Goto { target: START_BLOCK };
                                        }
                                    }
                                    body = &body_mem;
                                }
                                (capture_kind, args.upvar_tys().len())
                            } else { (CaptureKind::None, 0) };
                        checked_places.record_debuginfo(&body.var_debug_info);
                        let self_assignment =
                            find_self_assignments(&checked_places, body);
                        let mut live =
                            MaybeLivePlaces {
                                        tcx,
                                        capture_kind,
                                        checked_places: &checked_places,
                                        self_assignment,
                                    }.iterate_to_fixpoint(tcx, body,
                                    None).into_results_cursor(body);
                        let typing_env =
                            ty::TypingEnv::post_analysis(tcx, body.source.def_id());
                        let mut assignments =
                            AssignmentResult::find_dead_assignments(tcx, typing_env,
                                &checked_places, &mut live, body);
                        assignments.merge_guards();
                        let dead_captures =
                            assignments.compute_dead_captures(num_captures);
                        assignments.report_fully_unused();
                        assignments.report_unused_assignments();
                        dead_captures
                    }
                })();
{
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_mir_transform/src/liveness.rs:53",
                        "rustc_mir_transform::liveness", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_mir_transform/src/liveness.rs"),
                        ::tracing_core::__macro_support::Option::Some(53u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_transform::liveness"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("return")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("return");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&x)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};
x;#[tracing::instrument(level = "debug", skip(tcx), ret)]
54pub(crate) fn check_liveness<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> DenseBitSet<FieldIdx> {
55    // Don't run on synthetic MIR, as that will ICE trying to access HIR.
56    if tcx.is_synthetic_mir(def_id) {
57        return DenseBitSet::new_empty(0);
58    }
59
60    // Don't run unused pass for intrinsics
61    if tcx.intrinsic(def_id.to_def_id()).is_some() {
62        return DenseBitSet::new_empty(0);
63    }
64
65    // Don't run unused pass for #[naked]
66    if find_attr!(tcx, def_id.to_def_id(), Naked(..)) {
67        return DenseBitSet::new_empty(0);
68    }
69
70    // Don't run unused pass for #[derive]
71    let parent = tcx.local_parent(tcx.typeck_root_def_id_local(def_id));
72    if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent)
73        && find_attr!(tcx, parent, AutomaticallyDerived)
74    {
75        return DenseBitSet::new_empty(0);
76    }
77
78    let mut body = &*tcx.mir_promoted(def_id).0.borrow();
79    let mut body_mem;
80
81    // Don't run if there are errors.
82    if body.tainted_by_errors.is_some() {
83        return DenseBitSet::new_empty(0);
84    }
85
86    let mut checked_places = PlaceSet::default();
87    checked_places.insert_locals(&body.local_decls);
88
89    // The body is the one of a closure or generator, so we also want to analyse captures.
90    let (capture_kind, num_captures) = if tcx.is_closure_like(def_id.to_def_id()) {
91        let mut self_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
92        let mut self_is_ref = false;
93        if let ty::Ref(_, ty, _) = self_ty.kind() {
94            self_ty = *ty;
95            self_is_ref = true;
96        }
97
98        let (capture_kind, args) = match self_ty.kind() {
99            ty::Closure(_, args) => {
100                (CaptureKind::Closure(args.as_closure().kind()), ty::UpvarArgs::Closure(args))
101            }
102            &ty::Coroutine(_, args) => (CaptureKind::Coroutine, ty::UpvarArgs::Coroutine(args)),
103            &ty::CoroutineClosure(_, args) => {
104                (CaptureKind::CoroutineClosure, ty::UpvarArgs::CoroutineClosure(args))
105            }
106            _ => bug!("expected closure or generator, found {:?}", self_ty),
107        };
108
109        let captures = tcx.closure_captures(def_id);
110        checked_places.insert_captures(tcx, self_is_ref, captures, args.upvar_tys());
111
112        // `FnMut` closures can modify captured values and carry those
113        // modified values with them in subsequent calls. To model this behaviour,
114        // we consider the `FnMut` closure as jumping to `bb0` upon return.
115        if let CaptureKind::Closure(ty::ClosureKind::FnMut) = capture_kind {
116            // FIXME: stop cloning the body.
117            body_mem = body.clone();
118            for bbdata in body_mem.basic_blocks_mut() {
119                // We can call a closure again, either after a normal return or an unwind.
120                if let TerminatorKind::Return | TerminatorKind::UnwindResume =
121                    bbdata.terminator().kind
122                {
123                    bbdata.terminator_mut().kind = TerminatorKind::Goto { target: START_BLOCK };
124                }
125            }
126            body = &body_mem;
127        }
128
129        (capture_kind, args.upvar_tys().len())
130    } else {
131        (CaptureKind::None, 0)
132    };
133
134    // Get the remaining variables' names from debuginfo.
135    checked_places.record_debuginfo(&body.var_debug_info);
136
137    let self_assignment = find_self_assignments(&checked_places, body);
138
139    let mut live =
140        MaybeLivePlaces { tcx, capture_kind, checked_places: &checked_places, self_assignment }
141            .iterate_to_fixpoint(tcx, body, None)
142            .into_results_cursor(body);
143
144    let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id());
145
146    let mut assignments =
147        AssignmentResult::find_dead_assignments(tcx, typing_env, &checked_places, &mut live, body);
148
149    assignments.merge_guards();
150
151    let dead_captures = assignments.compute_dead_captures(num_captures);
152
153    assignments.report_fully_unused();
154    assignments.report_unused_assignments();
155
156    dead_captures
157}
158
159/// Small helper to make semantics easier to read.
160#[inline]
161fn is_capture(place: PlaceRef<'_>) -> bool {
162    if !place.projection.is_empty() {
163        if true {
    {
        match (&place.local, &ty::CAPTURE_STRUCT_LOCAL) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(place.local, ty::CAPTURE_STRUCT_LOCAL);
164        true
165    } else {
166        false
167    }
168}
169
170/// Give a diagnostic when an unused variable may be a typo of a unit variant or a struct.
171fn maybe_suggest_unit_pattern_typo<'tcx>(
172    tcx: TyCtxt<'tcx>,
173    body_def_id: DefId,
174    name: Symbol,
175    span: Span,
176    ty: Ty<'tcx>,
177) -> Option<diagnostics::PatternTypo> {
178    if let ty::Adt(adt_def, _) = ty.peel_refs().kind() {
179        let variant_names: Vec<_> = adt_def
180            .variants()
181            .iter()
182            .filter(|v| #[allow(non_exhaustive_omitted_patterns)] match v.ctor {
    Some((CtorKind::Const, _)) => true,
    _ => false,
}matches!(v.ctor, Some((CtorKind::Const, _))))
183            .map(|v| v.name)
184            .collect();
185        if let Some(name) = find_best_match_for_name(&variant_names, name, None)
186            && let Some(variant) = adt_def
187                .variants()
188                .iter()
189                .find(|v| v.name == name && #[allow(non_exhaustive_omitted_patterns)] match v.ctor {
    Some((CtorKind::Const, _)) => true,
    _ => false,
}matches!(v.ctor, Some((CtorKind::Const, _))))
190        {
191            return Some(diagnostics::PatternTypo {
192                span,
193                code: { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(variant.def_id) }with_no_trimmed_paths!(tcx.def_path_str(variant.def_id)),
194                kind: tcx.def_descr(variant.def_id),
195                item_name: variant.name,
196            });
197        }
198    }
199
200    // Look for consts of the same type with similar names as well,
201    // not just unit structs and variants.
202    let constants = tcx
203        .hir_body_owners()
204        .filter(|&def_id| {
205            tcx.def_kind(def_id) == DefKind::Const
206                && tcx.type_of(def_id).instantiate_identity().skip_norm_wip() == ty
207                && tcx.visibility(def_id).is_accessible_from(body_def_id, tcx)
208        })
209        .collect::<Vec<_>>();
210    let names = constants.iter().map(|&def_id| tcx.item_name(def_id)).collect::<Vec<_>>();
211    if let Some(item_name) = find_best_match_for_name(&names, name, None)
212        && let Some(position) = names.iter().position(|&n| n == item_name)
213        && let Some(&def_id) = constants.get(position)
214    {
215        return Some(diagnostics::PatternTypo {
216            span,
217            code: { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(def_id) }with_no_trimmed_paths!(tcx.def_path_str(def_id)),
218            kind: "constant",
219            item_name,
220        });
221    }
222
223    None
224}
225
226/// Return whether we should consider the current place as a drop guard and skip reporting.
227fn maybe_drop_guard<'tcx>(
228    tcx: TyCtxt<'tcx>,
229    typing_env: ty::TypingEnv<'tcx>,
230    index: PlaceIndex,
231    ever_dropped: &DenseBitSet<PlaceIndex>,
232    checked_places: &PlaceSet<'tcx>,
233    body: &Body<'tcx>,
234) -> bool {
235    if ever_dropped.contains(index) {
236        let ty = checked_places.places[index].ty(&body.local_decls, tcx).ty;
237        // FIXME(#155345): Liveness uses `TypingMode::PostAnalysis`
238        // even though it's run on `mir_promoted` which is still
239        // in an earlier `TypingMode`. This is odd and we have to
240        // manually mark aliases as non-rigid here.
241        let ty = ty::set_aliases_to_non_rigid(tcx, ty).skip_norm_wip();
242        #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
    ty::Closure(..) | ty::Coroutine(..) | ty::Tuple(..) | ty::Adt(..) |
        ty::Dynamic(..) | ty::Array(..) | ty::Slice(..) |
        ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => true,
    _ => false,
}matches!(
243            ty.kind(),
244            ty::Closure(..)
245                | ty::Coroutine(..)
246                | ty::Tuple(..)
247                | ty::Adt(..)
248                | ty::Dynamic(..)
249                | ty::Array(..)
250                | ty::Slice(..)
251                | ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. })
252        ) && ty.needs_drop(tcx, typing_env)
253    } else {
254        false
255    }
256}
257
258/// Detect the following case
259///
260/// ```text
261/// fn change_object(mut a: &Ty) {
262///     let a = Ty::new();
263///     b = &a;
264/// }
265/// ```
266///
267/// where the user likely meant to modify the value behind there reference, use `a` as an out
268/// parameter, instead of mutating the local binding. When encountering this we suggest:
269///
270/// ```text
271/// fn change_object(a: &'_ mut Ty) {
272///     let a = Ty::new();
273///     *b = a;
274/// }
275/// ```
276fn annotate_mut_binding_to_immutable_binding<'tcx>(
277    tcx: TyCtxt<'tcx>,
278    place: PlaceRef<'tcx>,
279    body_def_id: LocalDefId,
280    assignment_span: Span,
281    body: &Body<'tcx>,
282) -> Option<diagnostics::UnusedAssignSuggestion> {
283    use rustc_hir as hir;
284    use rustc_hir::intravisit::{self, Visitor};
285
286    // Verify we have a mutable argument...
287    let local = place.as_local()?;
288    let LocalKind::Arg = body.local_kind(local) else { return None };
289    let Mutability::Mut = body.local_decls[local].mutability else { return None };
290
291    // ... with reference type...
292    let hir_param_index =
293        local.as_usize() - if tcx.is_closure_like(body_def_id.to_def_id()) { 2 } else { 1 };
294    let fn_decl = tcx.hir_node_by_def_id(body_def_id).fn_decl()?;
295    let ty = fn_decl.inputs[hir_param_index];
296    let hir::TyKind::Ref(lt, mut_ty) = ty.kind else { return None };
297
298    // ... as a binding pattern.
299    let hir_body = tcx.hir_maybe_body_owned_by(body_def_id)?;
300    let param = hir_body.params[hir_param_index];
301    let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = param.pat.kind else {
302        return None;
303    };
304
305    // Find the assignment to modify.
306    let mut finder = ExprFinder { assignment_span, lhs: None, rhs: None };
307    finder.visit_body(hir_body);
308    let lhs = finder.lhs?;
309    let rhs = finder.rhs?;
310
311    let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _mut, inner) = rhs.kind else { return None };
312
313    // Changes to the parameter's type.
314    let pre = if lt.ident.span.is_empty() { "" } else { " " };
315    let ty_span = if mut_ty.mutbl.is_mut() {
316        // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028).
317        None
318    } else {
319        // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty`
320        Some(mut_ty.ty.span.shrink_to_lo())
321    };
322
323    return Some(diagnostics::UnusedAssignSuggestion {
324        ty_span,
325        pre,
326        // Span of the `mut` before the binding.
327        ty_ref_span: param.pat.span.until(ident.span),
328        // Where to add a `*`.
329        pre_lhs_span: lhs.span.shrink_to_lo(),
330        // Where to remove the borrow.
331        rhs_borrow_span: rhs.span.until(inner.span),
332    });
333
334    #[derive(#[automatically_derived]
impl<'hir> ::core::fmt::Debug for ExprFinder<'hir> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "ExprFinder",
            "assignment_span", &self.assignment_span, "lhs", &self.lhs, "rhs",
            &&self.rhs)
    }
}Debug)]
335    struct ExprFinder<'hir> {
336        assignment_span: Span,
337        lhs: Option<&'hir hir::Expr<'hir>>,
338        rhs: Option<&'hir hir::Expr<'hir>>,
339    }
340    impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
341        fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
342            if expr.span == self.assignment_span
343                && let hir::ExprKind::Assign(lhs, rhs, _) = expr.kind
344            {
345                self.lhs = Some(lhs);
346                self.rhs = Some(rhs);
347            } else {
348                intravisit::walk_expr(self, expr)
349            }
350        }
351    }
352}
353
354/// Compute self-assignments of the form `a += b`.
355///
356/// MIR building generates 2 statements and 1 terminator for such assignments:
357/// - _temp = CheckedBinaryOp(a, b)
358/// - assert(!_temp.1)
359/// - a = _temp.0
360///
361/// This function tries to detect this pattern in order to avoid marking statement as a definition
362/// and use. This will let the analysis be dictated by the next use of `a`.
363///
364/// Note that we will still need to account for the use of `b`.
365fn find_self_assignments<'tcx>(
366    checked_places: &PlaceSet<'tcx>,
367    body: &Body<'tcx>,
368) -> FxHashSet<Location> {
369    let mut self_assign = FxHashSet::default();
370
371    const FIELD_0: FieldIdx = FieldIdx::from_u32(0);
372    const FIELD_1: FieldIdx = FieldIdx::from_u32(1);
373
374    for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
375        for (statement_index, stmt) in bb_data.statements.iter().enumerate() {
376            let StatementKind::Assign((first_place, rvalue)) = &stmt.kind else { continue };
377            match rvalue {
378                // For checked binary ops, the MIR builder inserts an assertion in between.
379                Rvalue::BinaryOp(
380                    BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow,
381                    (Operand::Copy(lhs), _),
382                ) => {
383                    // Checked binary ops only appear at the end of the block, before the assertion.
384                    if statement_index + 1 != bb_data.statements.len() {
385                        continue;
386                    }
387
388                    let TerminatorKind::Assert {
389                        cond, target, msg: AssertKind::Overflow(..), ..
390                    } = &bb_data.terminator().kind
391                    else {
392                        continue;
393                    };
394                    let Some(assign) = body.basic_blocks[*target].statements.first() else {
395                        continue;
396                    };
397                    let StatementKind::Assign((dest, Rvalue::Use(Operand::Move(temp), _))) =
398                        assign.kind
399                    else {
400                        continue;
401                    };
402
403                    if dest != *lhs {
404                        continue;
405                    }
406
407                    let Operand::Move(cond) = cond else { continue };
408                    let [PlaceElem::Field(FIELD_0, _)] = &temp.projection.as_slice() else {
409                        continue;
410                    };
411                    let [PlaceElem::Field(FIELD_1, _)] = &cond.projection.as_slice() else {
412                        continue;
413                    };
414
415                    // We ignore indirect self-assignment, because both occurrences of `dest` are uses.
416                    let is_indirect = checked_places
417                        .get(dest.as_ref())
418                        .is_some_and(|(_, projections)| is_indirect(projections));
419                    if is_indirect {
420                        continue;
421                    }
422
423                    if first_place.local == temp.local
424                        && first_place.local == cond.local
425                        && first_place.projection.is_empty()
426                    {
427                        // Original block
428                        self_assign.insert(Location {
429                            block: bb,
430                            statement_index: bb_data.statements.len() - 1,
431                        });
432                        self_assign.insert(Location {
433                            block: bb,
434                            statement_index: bb_data.statements.len(),
435                        });
436                        // Target block
437                        self_assign.insert(Location { block: *target, statement_index: 0 });
438                    }
439                }
440                // Straight self-assignment.
441                Rvalue::BinaryOp(op, (Operand::Copy(lhs), _)) => {
442                    if lhs != first_place {
443                        continue;
444                    }
445
446                    // We ignore indirect self-assignment, because both occurrences of `dest` are uses.
447                    let is_indirect = checked_places
448                        .get(first_place.as_ref())
449                        .map_or(false, |(_, projections)| is_indirect(projections));
450                    if is_indirect {
451                        continue;
452                    }
453
454                    self_assign.insert(Location { block: bb, statement_index });
455
456                    // Checked division verifies overflow before performing the division, so we
457                    // need to go and ignore this check in the predecessor block.
458                    if let BinOp::Div | BinOp::Rem = op
459                        && statement_index == 0
460                        && let &[pred] = body.basic_blocks.predecessors()[bb].as_slice()
461                        && let TerminatorKind::Assert { msg, .. } =
462                            &body.basic_blocks[pred].terminator().kind
463                        && let AssertKind::Overflow(..) = **msg
464                        && let len = body.basic_blocks[pred].statements.len()
465                        && len >= 2
466                    {
467                        // BitAnd of two checks.
468                        self_assign.insert(Location { block: pred, statement_index: len - 1 });
469                        // `lhs == MIN`.
470                        self_assign.insert(Location { block: pred, statement_index: len - 2 });
471                    }
472                }
473                _ => {}
474            }
475        }
476    }
477
478    self_assign
479}
480
481#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for PlaceSet<'tcx> {
    #[inline]
    fn default() -> Self {
        Self {
            places: ::core::default::Default::default(),
            names: ::core::default::Default::default(),
            locals: ::core::default::Default::default(),
            capture_field_pos: ::core::default::Default::default(),
            captures: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for PlaceSet<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field5_finish(f, "PlaceSet",
            "places", &self.places, "names", &self.names, "locals",
            &self.locals, "capture_field_pos", &self.capture_field_pos,
            "captures", &&self.captures)
    }
}Debug)]
482struct PlaceSet<'tcx> {
483    places: IndexVec<PlaceIndex, PlaceRef<'tcx>>,
484    names: IndexVec<PlaceIndex, Option<(Symbol, Span)>>,
485
486    /// Places corresponding to locals, common case.
487    locals: IndexVec<Local, Option<PlaceIndex>>,
488
489    // Handling of captures.
490    /// If `_1` is a reference, we need to add a `Deref` to the matched place.
491    capture_field_pos: usize,
492    /// Captured fields.
493    captures: IndexVec<FieldIdx, (PlaceIndex, bool)>,
494}
495
496impl<'tcx> PlaceSet<'tcx> {
497    fn insert_locals(&mut self, decls: &IndexVec<Local, LocalDecl<'tcx>>) {
498        self.locals = IndexVec::from_elem(None, &decls);
499        for (local, decl) in decls.iter_enumerated() {
500            // Record all user-written locals for the analysis.
501            // We also keep the `RefForGuard` locals (more on that below).
502            if let LocalInfo::User(BindingForm::Var(_) | BindingForm::RefForGuard(_)) =
503                decl.local_info()
504            {
505                let index = self.places.push(local.into());
506                self.locals[local] = Some(index);
507                let _index = self.names.push(None);
508                if true {
    {
        match (&index, &_index) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(index, _index);
509            }
510        }
511    }
512
513    fn insert_captures(
514        &mut self,
515        tcx: TyCtxt<'tcx>,
516        self_is_ref: bool,
517        captures: &[&'tcx ty::CapturedPlace<'tcx>],
518        upvars: &ty::List<Ty<'tcx>>,
519    ) {
520        // We should not track the environment local separately.
521        if true {
    {
        match (&self.locals[ty::CAPTURE_STRUCT_LOCAL], &None) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(self.locals[ty::CAPTURE_STRUCT_LOCAL], None);
522
523        let self_place = Place {
524            local: ty::CAPTURE_STRUCT_LOCAL,
525            projection: tcx.mk_place_elems(if self_is_ref { &[PlaceElem::Deref] } else { &[] }),
526        };
527        if self_is_ref {
528            self.capture_field_pos = 1;
529        }
530
531        for (f, (capture, ty)) in std::iter::zip(captures, upvars).enumerate() {
532            let f = FieldIdx::from_usize(f);
533            let elem = PlaceElem::Field(f, ty);
534            let by_ref = #[allow(non_exhaustive_omitted_patterns)] match capture.info.capture_kind {
    ty::UpvarCapture::ByRef(..) => true,
    _ => false,
}matches!(capture.info.capture_kind, ty::UpvarCapture::ByRef(..));
535            let place = if by_ref {
536                self_place.project_deeper(&[elem, PlaceElem::Deref], tcx)
537            } else {
538                self_place.project_deeper(&[elem], tcx)
539            };
540            let index = self.places.push(place.as_ref());
541            let _f = self.captures.push((index, by_ref));
542            if true {
    {
        match (&_f, &f) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(_f, f);
543
544            // Record a variable name from the capture, because it is much friendlier than the
545            // debuginfo name.
546            self.names.insert(
547                index,
548                (Symbol::intern(&capture.to_string(tcx)), capture.get_path_span(tcx)),
549            );
550        }
551    }
552
553    fn record_debuginfo(&mut self, var_debug_info: &Vec<VarDebugInfo<'tcx>>) {
554        let ignore_name = |name: Symbol| {
555            name == sym::empty || name == kw::SelfLower || name.as_str().starts_with('_')
556        };
557        for var_debug_info in var_debug_info {
558            if let VarDebugInfoContents::Place(place) = var_debug_info.value
559                && let Some(index) = self.locals[place.local]
560                && !ignore_name(var_debug_info.name)
561            {
562                self.names.get_or_insert_with(index, || {
563                    (var_debug_info.name, var_debug_info.source_info.span)
564                });
565            }
566        }
567
568        // Discard places that will not result in a diagnostic.
569        for index_opt in self.locals.iter_mut() {
570            if let Some(index) = *index_opt {
571                let remove = match self.names[index] {
572                    None => true,
573                    Some((name, _)) => ignore_name(name),
574                };
575                if remove {
576                    *index_opt = None;
577                }
578            }
579        }
580    }
581
582    #[inline]
583    fn get(&self, place: PlaceRef<'tcx>) -> Option<(PlaceIndex, &'tcx [PlaceElem<'tcx>])> {
584        if let Some(index) = self.locals[place.local] {
585            return Some((index, place.projection));
586        }
587        if place.local == ty::CAPTURE_STRUCT_LOCAL
588            && !self.captures.is_empty()
589            && self.capture_field_pos < place.projection.len()
590            && let PlaceElem::Field(f, _) = place.projection[self.capture_field_pos]
591            && let Some((index, by_ref)) = self.captures.get(f)
592        {
593            let mut start = self.capture_field_pos + 1;
594            if *by_ref {
595                // Account for an extra Deref.
596                start += 1;
597            }
598            // We may have an attempt to access `_1.f` as a shallow reborrow. Just ignore it.
599            if start <= place.projection.len() {
600                let projection = &place.projection[start..];
601                return Some((*index, projection));
602            }
603        }
604        None
605    }
606
607    fn iter(&self) -> impl Iterator<Item = (PlaceIndex, &PlaceRef<'tcx>)> {
608        self.places.iter_enumerated()
609    }
610
611    fn len(&self) -> usize {
612        self.places.len()
613    }
614}
615
616struct AssignmentResult<'a, 'tcx> {
617    tcx: TyCtxt<'tcx>,
618    typing_env: ty::TypingEnv<'tcx>,
619    checked_places: &'a PlaceSet<'tcx>,
620    body: &'a Body<'tcx>,
621    /// Set of locals that are live at least once. This is used to report fully unused locals.
622    ever_live: DenseBitSet<PlaceIndex>,
623    /// Set of locals that have a non-trivial drop. This is used to skip reporting unused
624    /// assignment if it would be used by the `Drop` impl.
625    ever_dropped: DenseBitSet<PlaceIndex>,
626    /// Set of assignments for each local. Here, assignment is understood in the AST sense. Any
627    /// MIR that may look like an assignment (Assign, DropAndReplace, Yield, Call) are considered.
628    ///
629    /// For each local, we return a map: for each source position, whether the statement is live
630    /// and which kind of access it performs. When we encounter multiple statements at the same
631    /// location, we only increase the liveness, in order to avoid false positives.
632    assignments: IndexVec<PlaceIndex, FxIndexMap<SourceInfo, Access>>,
633}
634
635impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
636    /// Collect all assignments to checked locals.
637    ///
638    /// Assignments are collected, even if they are live. Dead assignments are reported, and live
639    /// assignments are used to make diagnostics correct for match guards.
640    fn find_dead_assignments(
641        tcx: TyCtxt<'tcx>,
642        typing_env: ty::TypingEnv<'tcx>,
643        checked_places: &'a PlaceSet<'tcx>,
644        cursor: &mut ResultsCursor<'_, 'tcx, MaybeLivePlaces<'_, 'tcx>>,
645        body: &'a Body<'tcx>,
646    ) -> AssignmentResult<'a, 'tcx> {
647        let mut ever_live = DenseBitSet::new_empty(checked_places.len());
648        let mut ever_dropped = DenseBitSet::new_empty(checked_places.len());
649        let mut assignments = IndexVec::<PlaceIndex, FxIndexMap<_, _>>::from_elem(
650            Default::default(),
651            &checked_places.places,
652        );
653
654        let mut check_place = |place: Place<'tcx>,
655                               kind,
656                               source_info: SourceInfo,
657                               location: Location,
658                               live: &DenseBitSet<PlaceIndex>| {
659            if let Some((index, extra_projections)) = checked_places.get(place.as_ref()) {
660                if !is_indirect(extra_projections) {
661                    let is_direct = extra_projections.is_empty();
662                    match assignments[index].entry(source_info) {
663                        IndexEntry::Vacant(v) => {
664                            let access =
665                                Access { kind, location, live: live.contains(index), is_direct };
666                            v.insert(access);
667                        }
668                        IndexEntry::Occupied(mut o) => {
669                            // There were already a sighting. Mark this statement as live if it
670                            // was, to avoid false positives.
671                            o.get_mut().live |= live.contains(index);
672                            o.get_mut().is_direct &= is_direct;
673                        }
674                    }
675                }
676            }
677        };
678
679        let mut record_drop = |place: Place<'tcx>| {
680            if let Some((index, &[])) = checked_places.get(place.as_ref()) {
681                ever_dropped.insert(index);
682            }
683        };
684
685        for (bb, bb_data) in traversal::postorder(body) {
686            cursor.seek_to_block_end(bb);
687            let live = cursor.get();
688            ever_live.union(live);
689
690            let terminator = bb_data.terminator();
691            match &terminator.kind {
692                TerminatorKind::Call { destination: place, .. }
693                | TerminatorKind::Yield { resume_arg: place, .. } => {
694                    check_place(
695                        *place,
696                        AccessKind::Assign,
697                        terminator.source_info,
698                        body.terminator_loc(bb),
699                        live,
700                    );
701                    record_drop(*place)
702                }
703                TerminatorKind::Drop { place, .. } => record_drop(*place),
704                TerminatorKind::InlineAsm { operands, .. } => {
705                    for operand in operands {
706                        if let InlineAsmOperand::Out { place: Some(place), .. }
707                        | InlineAsmOperand::InOut { out_place: Some(place), .. } = operand
708                        {
709                            check_place(
710                                *place,
711                                AccessKind::Assign,
712                                terminator.source_info,
713                                body.terminator_loc(bb),
714                                live,
715                            );
716                        }
717                    }
718                }
719                _ => {}
720            }
721
722            for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
723                let location = Location { block: bb, statement_index };
724                cursor.seek_before_primary_effect(location);
725                let live = cursor.get();
726                ever_live.union(live);
727                match &statement.kind {
728                    StatementKind::Assign((place, _)) => {
729                        check_place(
730                            *place,
731                            AccessKind::Assign,
732                            statement.source_info,
733                            location,
734                            live,
735                        );
736                    }
737                    StatementKind::SetDiscriminant { place, .. } => {
738                        check_place(
739                            **place,
740                            AccessKind::Assign,
741                            statement.source_info,
742                            location,
743                            live,
744                        );
745                    }
746                    StatementKind::StorageLive(_)
747                    | StatementKind::StorageDead(_)
748                    | StatementKind::Coverage(_)
749                    | StatementKind::Intrinsic(_)
750                    | StatementKind::Nop
751                    | StatementKind::FakeRead(_)
752                    | StatementKind::PlaceMention(_)
753                    | StatementKind::ConstEvalCounter
754                    | StatementKind::BackwardIncompatibleDropHint { .. }
755                    | StatementKind::AscribeUserType(_, _) => (),
756                }
757            }
758        }
759
760        // Check liveness of function arguments on entry.
761        {
762            cursor.seek_to_block_start(START_BLOCK);
763            let live = cursor.get();
764            ever_live.union(live);
765
766            // Verify that arguments and captured values are useful.
767            for (index, place) in checked_places.iter() {
768                let kind = if is_capture(*place) {
769                    // This is a by-ref capture, an assignment to it will modify surrounding
770                    // environment, so we do not report it.
771                    if place.projection.last() == Some(&PlaceElem::Deref) {
772                        continue;
773                    }
774
775                    AccessKind::Capture
776                } else if body.local_kind(place.local) == LocalKind::Arg {
777                    AccessKind::Param
778                } else {
779                    continue;
780                };
781                let source_info = body.local_decls[place.local].source_info;
782                let access = Access {
783                    kind,
784                    location: Location::START,
785                    live: live.contains(index),
786                    is_direct: true,
787                };
788                assignments[index].insert(source_info, access);
789            }
790        }
791
792        AssignmentResult {
793            tcx,
794            typing_env,
795            checked_places,
796            ever_live,
797            ever_dropped,
798            assignments,
799            body,
800        }
801    }
802
803    /// Match guards introduce a different local to freeze the guarded value as immutable.
804    /// Having two locals, we need to make sure that we do not report an unused_variable
805    /// when the guard local is used but not the arm local, or vice versa, like in this example.
806    ///
807    ///    match 5 {
808    ///      x if x > 2 => {}
809    ///      ^    ^- This is `local`
810    ///      +------ This is `arm_local`
811    ///      _ => {}
812    ///    }
813    ///
814    fn merge_guards(&mut self) {
815        for (index, place) in self.checked_places.iter() {
816            let local = place.local;
817            if let &LocalInfo::User(BindingForm::RefForGuard(arm_local)) =
818                self.body.local_decls[local].local_info()
819            {
820                if true {
    if !place.projection.is_empty() {
        ::core::panicking::panic("assertion failed: place.projection.is_empty()")
    };
};debug_assert!(place.projection.is_empty());
821
822                // Local to use in the arm.
823                let Some((arm_index, _proj)) = self.checked_places.get(arm_local.into()) else {
824                    continue;
825                };
826                if true {
    {
        match (&index, &arm_index) {
            (left_val, right_val) => {
                if *left_val == *right_val {
                    let kind = ::core::panicking::AssertKind::Ne;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_ne!(index, arm_index);
827                if true {
    {
        match (&_proj, &&[]) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(_proj, &[]);
828
829                // Mark the arm local as used if the guard local is used.
830                if self.ever_live.contains(index) {
831                    self.ever_live.insert(arm_index);
832                }
833
834                // Some assignments are common to both locals in the source code.
835                // Sadly, we can only detect this using the `source_info`.
836                // Therefore, we loop over all the assignments we have for the guard local:
837                // - if they already appeared for the arm local, the assignment is live if one of the
838                //   two versions is live;
839                // - if it does not appear for the arm local, it happened inside the guard, so we add
840                //   it as-is.
841                let guard_assignments = std::mem::take(&mut self.assignments[index]);
842                let arm_assignments = &mut self.assignments[arm_index];
843                for (source_info, access) in guard_assignments {
844                    match arm_assignments.entry(source_info) {
845                        IndexEntry::Vacant(v) => {
846                            v.insert(access);
847                        }
848                        IndexEntry::Occupied(mut o) => {
849                            o.get_mut().live |= access.live;
850                        }
851                    }
852                }
853            }
854        }
855    }
856
857    /// Compute captures that are fully dead.
858    fn compute_dead_captures(&self, num_captures: usize) -> DenseBitSet<FieldIdx> {
859        // Report to caller the set of dead captures.
860        let mut dead_captures = DenseBitSet::new_empty(num_captures);
861        for (index, place) in self.checked_places.iter() {
862            if self.ever_live.contains(index) {
863                continue;
864            }
865
866            // This is a capture: pass information to the enclosing function.
867            if is_capture(*place) {
868                for p in place.projection {
869                    if let PlaceElem::Field(f, _) = p {
870                        dead_captures.insert(*f);
871                        break;
872                    }
873                }
874                continue;
875            }
876        }
877
878        dead_captures
879    }
880
881    /// Check if a local is referenced in any reachable basic block.
882    /// Variables in unreachable code (e.g., after `todo!()`) should not trigger unused warnings.
883    fn is_local_in_reachable_code(&self, local: Local) -> bool {
884        struct LocalVisitor {
885            target_local: Local,
886            found: bool,
887        }
888
889        impl<'tcx> Visitor<'tcx> for LocalVisitor {
890            fn visit_local(&mut self, local: Local, _context: PlaceContext, _location: Location) {
891                if local == self.target_local {
892                    self.found = true;
893                }
894            }
895        }
896
897        let mut visitor = LocalVisitor { target_local: local, found: false };
898        for (bb, bb_data) in traversal::postorder(self.body) {
899            visitor.visit_basic_block_data(bb, bb_data);
900            if visitor.found {
901                return true;
902            }
903        }
904
905        false
906    }
907
908    /// Check for source-level uses that may have been removed from reachable MIR.
909    /// For example:
910    /// ```rust
911    /// fn example() {
912    ///     let x = todo!();
913    ///     eprintln!("{x}");
914    /// }
915    /// ```
916    /// The use of x is unreachable, but we'll still want to know if x is used to correctly emit
917    /// unused variable warning.
918    fn is_local_used_in_source(&self, name: Symbol, def_span: Span) -> bool {
919        use rustc_hir as hir;
920        use rustc_hir::def::Res;
921        use rustc_hir::intravisit::{self, Visitor};
922
923        let Some(body_def_id) = self.body.source.def_id().as_local() else { return false };
924        let Some(hir_body) = self.tcx.hir_maybe_body_owned_by(body_def_id) else { return false };
925        let typeck_results = self.tcx.typeck(body_def_id);
926
927        struct LocalUseVisitor<'a, 'tcx> {
928            tcx: TyCtxt<'tcx>,
929            typeck_results: &'a ty::TypeckResults<'tcx>,
930            name: Symbol,
931            def_span: Span,
932            found: bool,
933        }
934
935        impl<'a, 'tcx> Visitor<'tcx> for LocalUseVisitor<'a, 'tcx> {
936            fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
937                if self.found {
938                    return;
939                }
940
941                if let hir::ExprKind::Path(qpath) = &expr.kind
942                    && let Res::Local(hir_id) = self.typeck_results.qpath_res(qpath, expr.hir_id)
943                    && self.tcx.hir_name(hir_id) == self.name
944                    && self.tcx.hir_span(hir_id) == self.def_span
945                {
946                    self.found = true;
947                    return;
948                }
949
950                intravisit::walk_expr(self, expr);
951            }
952        }
953
954        let mut visitor =
955            LocalUseVisitor { tcx: self.tcx, typeck_results, name, def_span, found: false };
956        visitor.visit_body(hir_body);
957        visitor.found
958    }
959
960    /// Report fully unused locals, and forget the corresponding assignments.
961    fn report_fully_unused(&mut self) {
962        let tcx = self.tcx;
963
964        // Give a diagnostic when any of the string constants look like a naked format string that
965        // would interpolate our dead local.
966        let mut string_constants_in_body = None;
967        let mut maybe_suggest_literal_matching_name = |name: Symbol| {
968            // Visiting MIR to enumerate string constants can be expensive, so cache the result.
969            let string_constants_in_body = string_constants_in_body.get_or_insert_with(|| {
970                struct LiteralFinder {
971                    found: Vec<(Span, String)>,
972                }
973
974                impl<'tcx> Visitor<'tcx> for LiteralFinder {
975                    fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
976                        if let ty::Ref(_, ref_ty, _) = constant.ty().kind()
977                            && ref_ty.kind() == &ty::Str
978                        {
979                            let rendered_constant = constant.const_.to_string();
980                            self.found.push((constant.span, rendered_constant));
981                        }
982                    }
983                }
984
985                let mut finder = LiteralFinder { found: ::alloc::vec::Vec::new()vec![] };
986                finder.visit_body(self.body);
987                finder.found
988            });
989
990            let brace_name = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{{{0}", name))
    })format!("{{{name}");
991            string_constants_in_body
992                .iter()
993                .filter(|(_, rendered_constant)| {
994                    rendered_constant
995                        .split(&brace_name)
996                        .any(|c| #[allow(non_exhaustive_omitted_patterns)] match c.chars().next() {
    Some('}' | ':') => true,
    _ => false,
}matches!(c.chars().next(), Some('}' | ':')))
997                })
998                .map(|&(lit, _)| diagnostics::UnusedVariableStringInterp { lit })
999                .collect::<Vec<_>>()
1000        };
1001
1002        // First, report fully unused locals.
1003        for (index, place) in self.checked_places.iter() {
1004            if self.ever_live.contains(index) {
1005                continue;
1006            }
1007
1008            // this is a capture: let the enclosing function report the unused variable.
1009            if is_capture(*place) {
1010                continue;
1011            }
1012
1013            let local = place.local;
1014            let decl = &self.body.local_decls[local];
1015
1016            if decl.from_compiler_desugaring() {
1017                continue;
1018            }
1019
1020            // Only report actual user-defined binding from now on.
1021            let LocalInfo::User(BindingForm::Var(binding)) = decl.local_info() else { continue };
1022            let Some(hir_id) = decl.source_info.scope.lint_root(&self.body.source_scopes) else {
1023                continue;
1024            };
1025
1026            let introductions = &binding.introductions;
1027
1028            let Some((name, def_span)) = self.checked_places.names[index] else { continue };
1029
1030            // #117284, when `ident_span` and `def_span` have different contexts
1031            // we can't provide a good suggestion, instead we pointed out the spans from macro
1032            let from_macro = def_span.from_expansion()
1033                && introductions.iter().any(|intro| intro.span.eq_ctxt(def_span));
1034
1035            let maybe_suggest_typo = || {
1036                if let LocalKind::Arg = self.body.local_kind(local) {
1037                    None
1038                } else {
1039                    maybe_suggest_unit_pattern_typo(
1040                        tcx,
1041                        self.body.source.def_id(),
1042                        name,
1043                        def_span,
1044                        decl.ty,
1045                    )
1046                }
1047            };
1048
1049            // the is_local_used_in_source is sufficient to check if the local is used in the source code,
1050            // but we keep the local_kind check for a cheap filter to avoid heavy check
1051            let is_used_after_uninitialized = self.body.local_kind(local) == LocalKind::Temp
1052                && #[allow(non_exhaustive_omitted_patterns)] match binding.opt_match_place {
    Some((None, _)) => true,
    _ => false,
}matches!(binding.opt_match_place, Some((None, _)))
1053                && self.is_local_used_in_source(name, def_span);
1054
1055            let statements = &mut self.assignments[index];
1056            if statements.is_empty() {
1057                if is_used_after_uninitialized {
1058                    // A local from `let PAT = ...` normally has an assignment recorded for the
1059                    // value it initializes. If no assignment was recorded in reachable MIR, the
1060                    // initializer did not complete. If the local still has a source-level use,
1061                    // that use was made unreachable by the diverging initializer.
1062                    continue;
1063                }
1064
1065                if !self.is_local_in_reachable_code(local) {
1066                    continue;
1067                }
1068
1069                let sugg = if from_macro {
1070                    diagnostics::UnusedVariableSugg::NoSugg { span: def_span, name }
1071                } else {
1072                    let typo = maybe_suggest_typo();
1073                    diagnostics::UnusedVariableSugg::TryPrefix { spans: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [def_span]))vec![def_span], name, typo }
1074                };
1075                tcx.emit_node_span_lint(
1076                    UNUSED_VARIABLES,
1077                    hir_id,
1078                    def_span,
1079                    diagnostics::UnusedVariable {
1080                        name,
1081                        string_interp: maybe_suggest_literal_matching_name(name),
1082                        sugg,
1083                    },
1084                );
1085                continue;
1086            }
1087
1088            // Idiomatic rust assigns a value to a local upon definition. However, we do not want to
1089            // warn twice, for the unused local and for the unused assignment. Therefore, we remove
1090            // from the list of assignments the ones that happen at the definition site.
1091            statements.retain(|source_info, _| {
1092                !binding.introductions.iter().any(|intro| intro.span == source_info.span)
1093            });
1094
1095            // Extra assignments that we recognize thanks to the initialization span. We need to
1096            // take care of macro contexts here to be accurate.
1097            if let Some((_, initializer_span)) = binding.opt_match_place {
1098                statements.retain(|source_info, _| {
1099                    let within = source_info.span.find_ancestor_inside(initializer_span);
1100                    let outer_initializer_span =
1101                        initializer_span.find_ancestor_in_same_ctxt(source_info.span);
1102                    within.is_none()
1103                        && outer_initializer_span.is_none_or(|s| !s.contains(source_info.span))
1104                });
1105            }
1106
1107            if !statements.is_empty() {
1108                // We have a dead local with outstanding assignments and with non-trivial drop.
1109                // This is probably a drop-guard, so we do not issue a warning there.
1110                if maybe_drop_guard(
1111                    tcx,
1112                    self.typing_env,
1113                    index,
1114                    &self.ever_dropped,
1115                    self.checked_places,
1116                    self.body,
1117                ) {
1118                    statements.retain(|_, access| access.is_direct);
1119                    if statements.is_empty() {
1120                        continue;
1121                    }
1122                }
1123
1124                let typo = maybe_suggest_typo();
1125                tcx.emit_node_span_lint(
1126                    UNUSED_VARIABLES,
1127                    hir_id,
1128                    def_span,
1129                    diagnostics::UnusedVarAssignedOnly { name, typo },
1130                );
1131                continue;
1132            }
1133
1134            // We do not have outstanding assignments, suggest renaming the binding.
1135            let spans = introductions.iter().map(|intro| intro.span).collect::<Vec<_>>();
1136
1137            let any_shorthand = introductions.iter().any(|intro| intro.is_shorthand);
1138
1139            let sugg = if any_shorthand {
1140                diagnostics::UnusedVariableSugg::TryIgnore {
1141                    name: name.to_ident_string(),
1142                    shorthands: introductions
1143                        .iter()
1144                        .filter_map(
1145                            |intro| if intro.is_shorthand { Some(intro.span) } else { None },
1146                        )
1147                        .collect(),
1148                    non_shorthands: introductions
1149                        .iter()
1150                        .filter_map(
1151                            |intro| {
1152                                if !intro.is_shorthand { Some(intro.span) } else { None }
1153                            },
1154                        )
1155                        .collect(),
1156                }
1157            } else if from_macro {
1158                diagnostics::UnusedVariableSugg::NoSugg { span: def_span, name }
1159            } else if !introductions.is_empty() {
1160                let typo = maybe_suggest_typo();
1161                diagnostics::UnusedVariableSugg::TryPrefix { name, typo, spans: spans.clone() }
1162            } else {
1163                let typo = maybe_suggest_typo();
1164                diagnostics::UnusedVariableSugg::TryPrefix { name, typo, spans: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [def_span]))vec![def_span] }
1165            };
1166
1167            tcx.emit_node_span_lint(
1168                UNUSED_VARIABLES,
1169                hir_id,
1170                spans,
1171                diagnostics::UnusedVariable {
1172                    name,
1173                    string_interp: maybe_suggest_literal_matching_name(name),
1174                    sugg,
1175                },
1176            );
1177        }
1178    }
1179
1180    /// Second, report unused assignments that do not correspond to initialization.
1181    /// Initializations have been removed in the previous loop reporting unused variables.
1182    fn report_unused_assignments(self) {
1183        let tcx = self.tcx;
1184
1185        for (index, statements) in self.assignments.into_iter_enumerated() {
1186            if statements.is_empty() {
1187                continue;
1188            }
1189
1190            let Some((name, decl_span)) = self.checked_places.names[index] else { continue };
1191
1192            let is_maybe_drop_guard = maybe_drop_guard(
1193                tcx,
1194                self.typing_env,
1195                index,
1196                &self.ever_dropped,
1197                self.checked_places,
1198                self.body,
1199            );
1200
1201            // By convention, underscore-prefixed bindings are allowed to be unused explicitly.
1202            if name.as_str().starts_with('_') {
1203                continue;
1204            }
1205
1206            let mut next_direct_assignments: Vec<(Span, Location)> = Vec::new();
1207            let mut dead_statements = Vec::with_capacity(statements.len());
1208
1209            for (source_info, Access { live, kind, is_direct, location }) in statements.into_iter()
1210            {
1211                let direct_assignment = kind == AccessKind::Assign && is_direct;
1212                let should_report = !live && (is_direct || !is_maybe_drop_guard);
1213
1214                let overwrite = if should_report && direct_assignment {
1215                    next_direct_assignments
1216                        .iter()
1217                        .rfind(|(_, overwrite_location)| {
1218                            location.is_predecessor_of(*overwrite_location, self.body)
1219                        })
1220                        .map(|&(overwrite_span, _)| diagnostics::UnusedAssignOverwrite {
1221                            assigned_span: source_info.span,
1222                            overwrite_span,
1223                            name,
1224                        })
1225                } else {
1226                    None
1227                };
1228
1229                if direct_assignment {
1230                    next_direct_assignments.push((source_info.span, location));
1231                }
1232
1233                if !should_report {
1234                    continue;
1235                }
1236                dead_statements.push((source_info, kind, is_direct, overwrite));
1237            }
1238
1239            // We probed MIR in reverse order for dataflow.
1240            // Emit diagnostics in source order instead.
1241            for (source_info, kind, is_direct, overwrite) in dead_statements.into_iter().rev() {
1242                // Report the dead assignment.
1243                let Some(hir_id) = source_info.scope.lint_root(&self.body.source_scopes) else {
1244                    continue;
1245                };
1246
1247                match kind {
1248                    AccessKind::Assign => {
1249                        let suggestion = annotate_mut_binding_to_immutable_binding(
1250                            tcx,
1251                            self.checked_places.places[index],
1252                            self.body.source.def_id().expect_local(),
1253                            source_info.span,
1254                            self.body,
1255                        );
1256                        let overwrite =
1257                            if suggestion.is_none() && is_direct { overwrite } else { None };
1258                        let help = suggestion.is_none() && overwrite.is_none();
1259                        tcx.emit_node_span_lint(
1260                            UNUSED_ASSIGNMENTS,
1261                            hir_id,
1262                            source_info.span,
1263                            diagnostics::UnusedAssign { name, overwrite, help, suggestion },
1264                        )
1265                    }
1266                    AccessKind::Param => tcx.emit_node_span_lint(
1267                        UNUSED_ASSIGNMENTS,
1268                        hir_id,
1269                        source_info.span,
1270                        diagnostics::UnusedAssignPassed { name },
1271                    ),
1272                    AccessKind::Capture => tcx.emit_node_span_lint(
1273                        UNUSED_ASSIGNMENTS,
1274                        hir_id,
1275                        decl_span,
1276                        diagnostics::UnusedCaptureMaybeCaptureRef { name },
1277                    ),
1278                }
1279            }
1280        }
1281    }
1282}
1283
1284#[automatically_derived]
impl ::core::marker::Copy for PlaceIndex { }
impl PlaceIndex {
    #[doc = r" Maximum value the index can take, as a `u32`."]
    pub const MAX_AS_U32: u32 = 0xFFFF_FF00;
    #[doc = r" Maximum value the index can take."]
    pub const MAX: Self = Self::from_u32(0xFFFF_FF00);
    #[doc = r" Zero value of the index."]
    pub const ZERO: Self = Self::from_u32(0);
    #[doc = r" Creates a new index from a given `usize`."]
    #[doc = r""]
    #[doc = r" # Panics"]
    #[doc = r""]
    #[doc = r" Will panic if `value` exceeds `MAX`."]
    #[inline]
    pub const fn from_usize(value: usize) -> Self {
        if !(value <= (0xFFFF_FF00 as usize)) {
            ::core::panicking::panic("assertion failed: value <= (0xFFFF_FF00 as usize)")
        };
        unsafe { Self::from_u32_unchecked(value as u32) }
    }
    #[doc = r" Creates a new index from a given `u32`."]
    #[doc = r""]
    #[doc = r" # Panics"]
    #[doc = r""]
    #[doc = r" Will panic if `value` exceeds `MAX`."]
    #[inline]
    pub const fn from_u32(value: u32) -> Self {
        if !(value <= 0xFFFF_FF00) {
            ::core::panicking::panic("assertion failed: value <= 0xFFFF_FF00")
        };
        unsafe { Self::from_u32_unchecked(value) }
    }
    #[doc = r" Creates a new index from a given `u16`."]
    #[doc = r""]
    #[doc = r" # Panics"]
    #[doc = r""]
    #[doc = r" Will panic if `value` exceeds `MAX`."]
    #[inline]
    pub const fn from_u16(value: u16) -> Self {
        let value = value as u32;
        if !(value <= 0xFFFF_FF00) {
            ::core::panicking::panic("assertion failed: value <= 0xFFFF_FF00")
        };
        unsafe { Self::from_u32_unchecked(value) }
    }
    #[doc = r" Creates a new index from a given `u32`."]
    #[doc = r""]
    #[doc = r" # Safety"]
    #[doc = r""]
    #[doc =
    r" The provided value must be less than or equal to the maximum value for the newtype."]
    #[doc =
    r" Providing a value outside this range is undefined due to layout restrictions."]
    #[doc = r""]
    #[doc = r" Prefer using `from_u32`."]
    #[inline]
    pub const unsafe fn from_u32_unchecked(value: u32) -> Self {
        Self {
            private_use_as_methods_instead: unsafe {
                std::mem::transmute(value)
            },
        }
    }
    #[doc = r" Extracts the value of this index as a `usize`."]
    #[inline]
    pub const fn index(self) -> usize { self.as_usize() }
    #[doc = r" Extracts the value of this index as a `u32`."]
    #[inline]
    pub const fn as_u32(self) -> u32 {
        unsafe { std::mem::transmute(self.private_use_as_methods_instead) }
    }
    #[doc = r" Extracts the value of this index as a `usize`."]
    #[inline]
    pub const fn as_usize(self) -> usize { self.as_u32() as usize }
}
impl std::ops::Add<usize> for PlaceIndex {
    type Output = Self;
    #[inline]
    fn add(self, other: usize) -> Self {
        Self::from_usize(self.index() + other)
    }
}
impl std::ops::AddAssign<usize> for PlaceIndex {
    #[inline]
    fn add_assign(&mut self, other: usize) { *self = *self + other; }
}
impl rustc_index::Idx for PlaceIndex {
    #[inline]
    fn new(value: usize) -> Self { Self::from_usize(value) }
    #[inline]
    fn index(self) -> usize { self.as_usize() }
}
impl From<PlaceIndex> for u32 {
    #[inline]
    fn from(v: PlaceIndex) -> u32 { v.as_u32() }
}
impl From<PlaceIndex> for usize {
    #[inline]
    fn from(v: PlaceIndex) -> usize { v.as_usize() }
}
impl From<usize> for PlaceIndex {
    #[inline]
    fn from(value: usize) -> Self { Self::from_usize(value) }
}
impl From<u32> for PlaceIndex {
    #[inline]
    fn from(value: u32) -> Self { Self::from_u32(value) }
}
impl ::std::cmp::Eq for PlaceIndex {}
impl ::std::cmp::PartialEq for PlaceIndex {
    fn eq(&self, other: &Self) -> bool { self.as_u32().eq(&other.as_u32()) }
}
impl ::std::marker::StructuralPartialEq for PlaceIndex {}
impl ::std::hash::Hash for PlaceIndex {
    fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
        self.as_u32().hash(state)
    }
}
impl ::std::fmt::Debug for PlaceIndex {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        fmt.write_fmt(format_args!("{0}", self.as_u32()))
    }
}rustc_index::newtype_index! {
1285    pub struct PlaceIndex {}
1286}
1287
1288impl DebugWithContext<MaybeLivePlaces<'_, '_>> for PlaceIndex {
1289    fn fmt_with(
1290        &self,
1291        ctxt: &MaybeLivePlaces<'_, '_>,
1292        f: &mut std::fmt::Formatter<'_>,
1293    ) -> std::fmt::Result {
1294        std::fmt::Debug::fmt(&ctxt.checked_places.places[*self], f)
1295    }
1296}
1297
1298pub struct MaybeLivePlaces<'a, 'tcx> {
1299    tcx: TyCtxt<'tcx>,
1300    checked_places: &'a PlaceSet<'tcx>,
1301    capture_kind: CaptureKind,
1302    self_assignment: FxHashSet<Location>,
1303}
1304
1305impl<'tcx> MaybeLivePlaces<'_, 'tcx> {
1306    fn transfer_function<'a>(
1307        &'a self,
1308        trans: &'a mut DenseBitSet<PlaceIndex>,
1309    ) -> TransferFunction<'a, 'tcx> {
1310        TransferFunction {
1311            tcx: self.tcx,
1312            checked_places: &self.checked_places,
1313            capture_kind: self.capture_kind,
1314            trans,
1315            self_assignment: &self.self_assignment,
1316        }
1317    }
1318}
1319
1320impl<'tcx> Analysis<'tcx> for MaybeLivePlaces<'_, 'tcx> {
1321    type Domain = DenseBitSet<PlaceIndex>;
1322    type Direction = Backward;
1323
1324    const NAME: &'static str = "liveness-lint";
1325
1326    fn bottom_value(&self, _: &Body<'tcx>) -> Self::Domain {
1327        // bottom = not live
1328        DenseBitSet::new_empty(self.checked_places.len())
1329    }
1330
1331    fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) {
1332        // No variables are live until we observe a use
1333    }
1334
1335    fn apply_primary_statement_effect(
1336        &self,
1337        trans: &mut Self::Domain,
1338        statement: &Statement<'tcx>,
1339        location: Location,
1340    ) {
1341        self.transfer_function(trans).visit_statement(statement, location);
1342    }
1343
1344    fn apply_primary_terminator_effect(
1345        &self,
1346        trans: &mut Self::Domain,
1347        terminator: &Terminator<'tcx>,
1348        location: Location,
1349    ) {
1350        self.transfer_function(trans).visit_terminator(terminator, location);
1351    }
1352
1353    fn apply_call_return_effect(
1354        &self,
1355        _trans: &mut Self::Domain,
1356        _block: BasicBlock,
1357        _return_places: CallReturnPlaces<'_, 'tcx>,
1358    ) {
1359        // FIXME: what should happen here?
1360    }
1361}
1362
1363struct TransferFunction<'a, 'tcx> {
1364    tcx: TyCtxt<'tcx>,
1365    checked_places: &'a PlaceSet<'tcx>,
1366    trans: &'a mut DenseBitSet<PlaceIndex>,
1367    capture_kind: CaptureKind,
1368    self_assignment: &'a FxHashSet<Location>,
1369}
1370
1371impl<'tcx> Visitor<'tcx> for TransferFunction<'_, 'tcx> {
1372    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1373        match statement.kind {
1374            // `ForLet(None)` and `ForGuardBinding` fake reads erroneously mark the just-assigned
1375            // locals as live. This defeats the purpose of the analysis for such bindings.
1376            StatementKind::FakeRead((
1377                FakeReadCause::ForLet(None) | FakeReadCause::ForGuardBinding,
1378                _,
1379            )) => return,
1380            // Handle self-assignment by restricting the read/write they do.
1381            StatementKind::Assign((ref dest, ref rvalue))
1382                if self.self_assignment.contains(&location) =>
1383            {
1384                if let Rvalue::BinaryOp(
1385                    BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow,
1386                    (_, rhs),
1387                ) = rvalue
1388                {
1389                    // We are computing the binary operation:
1390                    // - the LHS will be assigned, so we don't read it;
1391                    // - the RHS still needs to be read.
1392                    self.visit_operand(rhs, location);
1393                    self.visit_place(
1394                        dest,
1395                        PlaceContext::MutatingUse(MutatingUseContext::Store),
1396                        location,
1397                    );
1398                } else if let Rvalue::BinaryOp(_, (_, rhs)) = rvalue {
1399                    // We are computing the binary operation:
1400                    // - the LHS is being updated, so we don't read it;
1401                    // - the RHS still needs to be read.
1402                    self.visit_operand(rhs, location);
1403                } else {
1404                    // This is the second part of a checked self-assignment,
1405                    // we are assigning the result.
1406                    // We do not consider the write to the destination as a `def`.
1407                    // `self_assignment` must be false if the assignment is indirect.
1408                    self.visit_rvalue(rvalue, location);
1409                }
1410            }
1411            _ => self.super_statement(statement, location),
1412        }
1413    }
1414
1415    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
1416        // By-ref captures could be read by the surrounding environment, so we mark
1417        // them as live upon yield and return.
1418        match terminator.kind {
1419            TerminatorKind::Return
1420            | TerminatorKind::Yield { .. }
1421            | TerminatorKind::Goto { target: START_BLOCK } // Inserted for the `FnMut` case.
1422            | TerminatorKind::Call { target: None, .. } // unwinding could be caught
1423                if self.capture_kind != CaptureKind::None =>
1424            {
1425                // All indirect captures have an effect on the environment, so we mark them as live.
1426                for (index, place) in self.checked_places.iter() {
1427                    if place.local == ty::CAPTURE_STRUCT_LOCAL
1428                        && place.projection.last() == Some(&PlaceElem::Deref)
1429                    {
1430                        self.trans.insert(index);
1431                    }
1432                }
1433            }
1434            // Do not consider a drop to be a use. We whitelist interesting drops elsewhere.
1435            TerminatorKind::Drop { .. } => {}
1436            // Ignore assertions since they must be triggered by actual code.
1437            TerminatorKind::Assert { .. } => {}
1438            _ => self.super_terminator(terminator, location),
1439        }
1440    }
1441
1442    fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1443        match rvalue {
1444            // When a closure/generator does not use some of its captures, do not consider these
1445            // captures as live in the surrounding function. This allows to report unused variables,
1446            // even if they have been (uselessly) captured.
1447            Rvalue::Aggregate(
1448                AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _),
1449                operands,
1450            ) => {
1451                if let Some(def_id) = def_id.as_local() {
1452                    let dead_captures = self.tcx.check_liveness(def_id);
1453                    for (field, operand) in
1454                        operands.iter_enumerated().take(dead_captures.domain_size())
1455                    {
1456                        if !dead_captures.contains(field) {
1457                            self.visit_operand(operand, location);
1458                        }
1459                    }
1460                }
1461            }
1462            _ => self.super_rvalue(rvalue, location),
1463        }
1464    }
1465
1466    fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
1467        if let Some((index, extra_projections)) = self.checked_places.get(place.as_ref()) {
1468            for i in (extra_projections.len()..=place.projection.len()).rev() {
1469                let place_part =
1470                    PlaceRef { local: place.local, projection: &place.projection[..i] };
1471                let extra_projections = &place.projection[i..];
1472
1473                if let Some(&elem) = extra_projections.get(0) {
1474                    self.visit_projection_elem(place_part, elem, context, location);
1475                }
1476            }
1477
1478            match DefUse::for_place(extra_projections, context) {
1479                Some(DefUse::Def) => {
1480                    self.trans.remove(index);
1481                }
1482                Some(DefUse::Use) => {
1483                    self.trans.insert(index);
1484                }
1485                None => {}
1486            }
1487        } else {
1488            self.super_place(place, context, location)
1489        }
1490    }
1491
1492    fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
1493        if let Some((index, _proj)) = self.checked_places.get(local.into()) {
1494            if true {
    {
        match (&_proj, &&[]) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(_proj, &[]);
1495            match DefUse::for_place(&[], context) {
1496                Some(DefUse::Def) => {
1497                    self.trans.remove(index);
1498                }
1499                Some(DefUse::Use) => {
1500                    self.trans.insert(index);
1501                }
1502                _ => {}
1503            }
1504        }
1505    }
1506}
1507
1508#[derive(#[automatically_derived]
impl ::core::cmp::Eq for DefUse { }Eq, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for DefUse { }
#[automatically_derived]
impl ::core::cmp::PartialEq for DefUse {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::fmt::Debug for DefUse {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self { DefUse::Def => "Def", DefUse::Use => "Use", })
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for DefUse {
    #[inline]
    fn clone(&self) -> Self {
        match self { Self::Def => Self::Def, Self::Use => Self::Use, }
    }
}Clone)]
1509enum DefUse {
1510    Def,
1511    Use,
1512}
1513
1514fn is_indirect(proj: &[PlaceElem<'_>]) -> bool {
1515    proj.iter().any(|p| p.is_indirect())
1516}
1517
1518impl DefUse {
1519    fn for_place<'tcx>(projection: &[PlaceElem<'tcx>], context: PlaceContext) -> Option<DefUse> {
1520        let is_indirect = is_indirect(projection);
1521        match context {
1522            PlaceContext::MutatingUse(
1523                MutatingUseContext::Store | MutatingUseContext::SetDiscriminant,
1524            ) => {
1525                if is_indirect {
1526                    // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a
1527                    // use.
1528                    Some(DefUse::Use)
1529                } else if projection.is_empty() {
1530                    Some(DefUse::Def)
1531                } else {
1532                    None
1533                }
1534            }
1535
1536            // For the associated terminators, this is only a `Def` when the terminator returns
1537            // "successfully." As such, we handle this case separately in `call_return_effect`
1538            // above. However, if the place looks like `*_5`, this is still unconditionally a use of
1539            // `_5`.
1540            PlaceContext::MutatingUse(
1541                MutatingUseContext::Call
1542                | MutatingUseContext::Yield
1543                | MutatingUseContext::AsmOutput,
1544            ) => is_indirect.then_some(DefUse::Use),
1545
1546            // All other contexts are uses...
1547            PlaceContext::MutatingUse(
1548                MutatingUseContext::RawBorrow
1549                | MutatingUseContext::Borrow
1550                | MutatingUseContext::Drop,
1551            )
1552            | PlaceContext::NonMutatingUse(
1553                NonMutatingUseContext::RawBorrow
1554                | NonMutatingUseContext::Copy
1555                | NonMutatingUseContext::Inspect
1556                | NonMutatingUseContext::Move
1557                | NonMutatingUseContext::FakeBorrow
1558                | NonMutatingUseContext::SharedBorrow
1559                | NonMutatingUseContext::PlaceMention,
1560            ) => Some(DefUse::Use),
1561
1562            PlaceContext::NonUse(
1563                NonUseContext::StorageLive
1564                | NonUseContext::StorageDead
1565                | NonUseContext::AscribeUserTy(_)
1566                | NonUseContext::BackwardIncompatibleDropHint
1567                | NonUseContext::VarDebugInfo,
1568            ) => None,
1569
1570            PlaceContext::MutatingUse(MutatingUseContext::Projection)
1571            | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
1572                {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("A projection could be a def or a use and must be handled separately")));
}unreachable!("A projection could be a def or a use and must be handled separately")
1573            }
1574        }
1575    }
1576}