1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use rustc::mir::visit::{PlaceContext, Visitor};
12 use rustc::mir::{BasicBlock, Local, Location, Place, Statement, StatementKind, TerminatorKind};
14 use rustc_data_structures::fx::FxHashSet;
16 use borrow_check::MirBorrowckCtxt;
18 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
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.
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.
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(
36 temporary_used_locals: FxHashSet<Local>,
37 mut never_initialized_mut_locals: FxHashSet<Local>,
40 let mut visitor = GatherUsedMutsVisitor {
41 temporary_used_locals,
42 never_initialized_mut_locals: &mut never_initialized_mut_locals,
45 visitor.visit_mir(visitor.mbcx.mir);
48 // Take the union of the existed `used_mut` set with those variables we've found were
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();
55 /// MIR visitor for collecting used mutable variables.
56 /// The 'visit lifetime represents the duration of the MIR walk.
57 struct GatherUsedMutsVisitor<'visit, 'cx: 'visit, 'gcx: 'tcx, 'tcx: 'cx> {
58 temporary_used_locals: FxHashSet<Local>,
59 never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
60 mbcx: &'visit mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
63 impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
64 fn visit_terminator_kind(
67 kind: &TerminatorKind<'tcx>,
70 debug!("visit_terminator_kind: kind={:?}", kind);
72 TerminatorKind::Call { destination: Some((into, _)), .. } => {
73 if let Some(local) = into.base_local() {
75 "visit_terminator_kind: kind={:?} local={:?} \
76 never_initialized_mut_locals={:?}",
77 kind, local, self.never_initialized_mut_locals
79 let _ = self.never_initialized_mut_locals.remove(&local);
89 statement: &Statement<'tcx>,
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() {
101 "visit_statement: statement={:?} local={:?} \
102 never_initialized_mut_locals={:?}",
103 statement, local, self.never_initialized_mut_locals
105 let _ = self.never_initialized_mut_locals.remove(&local);
115 place_context: PlaceContext<'tcx>,
118 if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
119 // Propagate the Local assigned at this Location as a used mutable local variable
120 for moi in &self.mbcx.move_data.loc_map[location] {
121 let mpi = &self.mbcx.move_data.moves[*moi].path;
122 let path = &self.mbcx.move_data.move_paths[*mpi];
124 "assignment of {:?} to {:?}, adding {:?} to used mutable set",
125 path.place, local, path.place
127 if let Place::Local(user_local) = path.place {
128 self.mbcx.used_mut.insert(user_local);