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