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