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