]>
Commit | Line | Data |
---|---|---|
487cf647 FG |
1 | #![deny(rustc::untranslatable_diagnostic)] |
2 | #![deny(rustc::diagnostic_outside_of_impl)] | |
353b0b11 | 3 | use rustc_data_structures::fx::FxIndexSet; |
ba9703b0 | 4 | use rustc_middle::mir::visit::{PlaceContext, Visitor}; |
f035d41b XL |
5 | use rustc_middle::mir::{ |
6 | Local, Location, Place, Statement, StatementKind, Terminator, TerminatorKind, | |
7 | }; | |
8faf50e0 | 8 | |
c295e0f8 | 9 | use crate::MirBorrowckCtxt; |
8faf50e0 | 10 | |
dc9dc135 | 11 | impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |
a1dfa0c6 XL |
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. | |
923072b8 | 27 | pub(crate) fn gather_used_muts( |
a1dfa0c6 | 28 | &mut self, |
353b0b11 FG |
29 | temporary_used_locals: FxIndexSet<Local>, |
30 | mut never_initialized_mut_locals: FxIndexSet<Local>, | |
a1dfa0c6 XL |
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 | }; | |
4b012472 | 38 | visitor.visit_body(visitor.mbcx.body); |
a1dfa0c6 XL |
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(); | |
8faf50e0 XL |
45 | } |
46 | } | |
47 | ||
a1dfa0c6 XL |
48 | /// MIR visitor for collecting used mutable variables. |
49 | /// The 'visit lifetime represents the duration of the MIR walk. | |
dc9dc135 | 50 | struct GatherUsedMutsVisitor<'visit, 'cx, 'tcx> { |
353b0b11 FG |
51 | temporary_used_locals: FxIndexSet<Local>, |
52 | never_initialized_mut_locals: &'visit mut FxIndexSet<Local>, | |
dc9dc135 | 53 | mbcx: &'visit mut MirBorrowckCtxt<'cx, 'tcx>, |
8faf50e0 XL |
54 | } |
55 | ||
dc9dc135 | 56 | impl GatherUsedMutsVisitor<'_, '_, '_> { |
ba9703b0 | 57 | fn remove_never_initialized_mut_locals(&mut self, into: Place<'_>) { |
dc9dc135 XL |
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. | |
dfeec247 | 63 | self.never_initialized_mut_locals.remove(&into.local); |
dc9dc135 XL |
64 | } |
65 | } | |
66 | ||
67 | impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tcx> { | |
f035d41b XL |
68 | fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { |
69 | debug!("visit_terminator: terminator={:?}", terminator); | |
70 | match &terminator.kind { | |
923072b8 FG |
71 | TerminatorKind::Call { destination, .. } => { |
72 | self.remove_never_initialized_mut_locals(*destination); | |
dfeec247 | 73 | } |
dfeec247 | 74 | _ => {} |
a1dfa0c6 | 75 | } |
f035d41b XL |
76 | |
77 | self.super_terminator(terminator, location); | |
a1dfa0c6 XL |
78 | } |
79 | ||
f035d41b | 80 | fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { |
ba9703b0 XL |
81 | if let StatementKind::Assign(box (into, _)) = &statement.kind { |
82 | debug!( | |
83 | "visit_statement: statement={:?} local={:?} \ | |
dfeec247 | 84 | never_initialized_mut_locals={:?}", |
ba9703b0 XL |
85 | statement, into.local, self.never_initialized_mut_locals |
86 | ); | |
87 | self.remove_never_initialized_mut_locals(*into); | |
a1dfa0c6 | 88 | } |
f035d41b XL |
89 | |
90 | self.super_statement(statement, location); | |
a1dfa0c6 XL |
91 | } |
92 | ||
064997fb FG |
93 | fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) { |
94 | if place_context.is_place_assignment() && self.temporary_used_locals.contains(&local) { | |
8faf50e0 XL |
95 | // Propagate the Local assigned at this Location as a used mutable local variable |
96 | for moi in &self.mbcx.move_data.loc_map[location] { | |
97 | let mpi = &self.mbcx.move_data.moves[*moi].path; | |
98 | let path = &self.mbcx.move_data.move_paths[*mpi]; | |
99 | debug!( | |
100 | "assignment of {:?} to {:?}, adding {:?} to used mutable set", | |
101 | path.place, local, path.place | |
102 | ); | |
e74abb32 | 103 | if let Some(user_local) = path.place.as_local() { |
8faf50e0 XL |
104 | self.mbcx.used_mut.insert(user_local); |
105 | } | |
106 | } | |
107 | } | |
108 | } | |
109 | } |