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