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