1 use rustc_middle
::mir
::visit
::{PlaceContext, Visitor}
;
2 use rustc_middle
::mir
::{
3 Local
, Location
, Place
, Statement
, StatementKind
, Terminator
, TerminatorKind
,
6 use rustc_data_structures
::fx
::FxHashSet
;
8 use crate::borrow_check
::MirBorrowckCtxt
;
10 impl<'cx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'tcx
> {
11 /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
12 /// of the `unused_mut` lint.
14 /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
15 /// used from borrow checking. This function looks for assignments into these locals from
16 /// user-declared locals and adds those user-defined locals to the `used_mut` set. This can
17 /// occur due to a rare case involving upvars in closures.
19 /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
20 /// (not arguments) that have not already been marked as being used.
21 /// This function then looks for assignments from statements or the terminator into the locals
22 /// from this set and removes them from the set. This leaves only those locals that have not
23 /// been assigned to - this set is used as a proxy for locals that were not initialized due to
24 /// unreachable code. These locals are then considered "used" to silence the lint for them.
25 /// See #55344 for context.
26 crate fn gather_used_muts(
28 temporary_used_locals
: FxHashSet
<Local
>,
29 mut never_initialized_mut_locals
: FxHashSet
<Local
>,
32 let mut visitor
= GatherUsedMutsVisitor
{
33 temporary_used_locals
,
34 never_initialized_mut_locals
: &mut never_initialized_mut_locals
,
37 visitor
.visit_body(&visitor
.mbcx
.body
);
40 // Take the union of the existed `used_mut` set with those variables we've found were
42 debug
!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals
);
43 self.used_mut
= self.used_mut
.union(&never_initialized_mut_locals
).cloned().collect();
47 /// MIR visitor for collecting used mutable variables.
48 /// The 'visit lifetime represents the duration of the MIR walk.
49 struct GatherUsedMutsVisitor
<'visit
, 'cx
, 'tcx
> {
50 temporary_used_locals
: FxHashSet
<Local
>,
51 never_initialized_mut_locals
: &'visit
mut FxHashSet
<Local
>,
52 mbcx
: &'visit
mut MirBorrowckCtxt
<'cx
, 'tcx
>,
55 impl GatherUsedMutsVisitor
<'_
, '_
, '_
> {
56 fn remove_never_initialized_mut_locals(&mut self, into
: Place
<'_
>) {
57 // Remove any locals that we found were initialized from the
58 // `never_initialized_mut_locals` set. At the end, the only remaining locals will
59 // be those that were never initialized - we will consider those as being used as
60 // they will either have been removed by unreachable code optimizations; or linted
61 // as unused variables.
62 self.never_initialized_mut_locals
.remove(&into
.local
);
66 impl<'visit
, 'cx
, 'tcx
> Visitor
<'tcx
> for GatherUsedMutsVisitor
<'visit
, 'cx
, 'tcx
> {
67 fn visit_terminator(&mut self, terminator
: &Terminator
<'tcx
>, location
: Location
) {
68 debug
!("visit_terminator: terminator={:?}", terminator
);
69 match &terminator
.kind
{
70 TerminatorKind
::Call { destination: Some((into, _)), .. }
=> {
71 self.remove_never_initialized_mut_locals(*into
);
73 TerminatorKind
::DropAndReplace { place, .. }
=> {
74 self.remove_never_initialized_mut_locals(*place
);
79 self.super_terminator(terminator
, location
);
82 fn visit_statement(&mut self, statement
: &Statement
<'tcx
>, location
: Location
) {
83 if let StatementKind
::Assign(box (into
, _
)) = &statement
.kind
{
85 "visit_statement: statement={:?} local={:?} \
86 never_initialized_mut_locals={:?}",
87 statement
, into
.local
, self.never_initialized_mut_locals
89 self.remove_never_initialized_mut_locals(*into
);
92 self.super_statement(statement
, location
);
95 fn visit_local(&mut self, local
: &Local
, place_context
: PlaceContext
, location
: Location
) {
96 if place_context
.is_place_assignment() && self.temporary_used_locals
.contains(local
) {
97 // Propagate the Local assigned at this Location as a used mutable local variable
98 for moi
in &self.mbcx
.move_data
.loc_map
[location
] {
99 let mpi
= &self.mbcx
.move_data
.moves
[*moi
].path
;
100 let path
= &self.mbcx
.move_data
.move_paths
[*mpi
];
102 "assignment of {:?} to {:?}, adding {:?} to used mutable set",
103 path
.place
, local
, path
.place
105 if let Some(user_local
) = path
.place
.as_local() {
106 self.mbcx
.used_mut
.insert(user_local
);