]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/used_muts.rs
2587d14a73a8fe9df62ed74f4f641991e16ed6b3
[rust.git] / src / librustc_mir / borrow_check / used_muts.rs
1 use rustc::mir::visit::{PlaceContext, Visitor};
2 use rustc::mir::{
3     Local, Location, Place, PlaceBase, Statement, StatementKind, TerminatorKind
4 };
5
6 use rustc_data_structures::fx::FxHashSet;
7
8 use crate::borrow_check::MirBorrowckCtxt;
9
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.
13     ///
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.
18     ///
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(
27         &mut self,
28         temporary_used_locals: FxHashSet<Local>,
29         mut never_initialized_mut_locals: FxHashSet<Local>,
30     ) {
31         {
32             let mut visitor = GatherUsedMutsVisitor {
33                 temporary_used_locals,
34                 never_initialized_mut_locals: &mut never_initialized_mut_locals,
35                 mbcx: self,
36             };
37             visitor.visit_body(visitor.mbcx.body);
38         }
39
40         // Take the union of the existed `used_mut` set with those variables we've found were
41         // never initialized.
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();
44     }
45 }
46
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>,
53 }
54
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         if let PlaceBase::Local(local) = into.base {
63             let _ = self.never_initialized_mut_locals.remove(&local);
64         }
65     }
66 }
67
68 impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tcx> {
69     fn visit_terminator_kind(
70         &mut self,
71         kind: &TerminatorKind<'tcx>,
72         _location: Location,
73     ) {
74         debug!("visit_terminator_kind: kind={:?}", kind);
75         match &kind {
76             TerminatorKind::Call { destination: Some((into, _)), .. } => {
77                 self.remove_never_initialized_mut_locals(&into);
78             },
79             TerminatorKind::DropAndReplace { location, .. } => {
80                 self.remove_never_initialized_mut_locals(&location);
81             },
82             _ => {},
83         }
84     }
85
86     fn visit_statement(
87         &mut self,
88         statement: &Statement<'tcx>,
89         _location: Location,
90     ) {
91         match &statement.kind {
92             StatementKind::Assign(into, _) => {
93                 if let PlaceBase::Local(local) = into.base {
94                     debug!(
95                         "visit_statement: statement={:?} local={:?} \
96                          never_initialized_mut_locals={:?}",
97                         statement, local, self.never_initialized_mut_locals
98                     );
99                 }
100                 self.remove_never_initialized_mut_locals(into);
101             },
102             _ => {},
103         }
104     }
105
106     fn visit_local(
107         &mut self,
108         local: &Local,
109         place_context: PlaceContext,
110         location: Location,
111     ) {
112         if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
113             // Propagate the Local assigned at this Location as a used mutable local variable
114             for moi in &self.mbcx.move_data.loc_map[location] {
115                 let mpi = &self.mbcx.move_data.moves[*moi].path;
116                 let path = &self.mbcx.move_data.move_paths[*mpi];
117                 debug!(
118                     "assignment of {:?} to {:?}, adding {:?} to used mutable set",
119                     path.place, local, path.place
120                 );
121                 if let Place {
122                     base: PlaceBase::Local(user_local),
123                     projection: None,
124                 } = path.place {
125                     self.mbcx.used_mut.insert(user_local);
126                 }
127             }
128         }
129     }
130 }