1use rustc_abi::FieldIdx;
2use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry};
3use rustc_hir::def::{CtorKind, DefKind};
4use rustc_hir::def_id::{DefId, LocalDefId};
5use rustc_hir::find_attr;
6use rustc_index::IndexVec;
7use rustc_index::bit_set::DenseBitSet;
8use rustc_middle::bug;
9use rustc_middle::mir::visit::{
10 MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, Visitor,
11};
12use rustc_middle::mir::*;
13use rustc_middle::ty::print::with_no_trimmed_paths;
14use rustc_middle::ty::{self, Ty, TyCtxt};
15use rustc_mir_dataflow::fmt::DebugWithContext;
16use rustc_mir_dataflow::{Analysis, Backward, ResultsCursor};
17use rustc_session::lint;
18use rustc_span::Span;
19use rustc_span::edit_distance::find_best_match_for_name;
20use rustc_span::symbol::{Symbol, kw, sym};
21
22use crate::errors;
23
24#[derive(#[automatically_derived]
impl ::core::marker::Copy for AccessKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for AccessKind {
#[inline]
fn clone(&self) -> AccessKind { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for AccessKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
AccessKind::Param => "Param",
AccessKind::Assign => "Assign",
AccessKind::Capture => "Capture",
})
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for AccessKind {
#[inline]
fn eq(&self, other: &AccessKind) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for AccessKind {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq)]
25enum AccessKind {
26 Param,
27 Assign,
28 Capture,
29}
30
31#[derive(#[automatically_derived]
impl ::core::marker::Copy for CaptureKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for CaptureKind {
#[inline]
fn clone(&self) -> CaptureKind {
let _: ::core::clone::AssertParamIsClone<ty::ClosureKind>;
*self
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for CaptureKind {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
CaptureKind::Closure(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Closure", &__self_0),
CaptureKind::Coroutine =>
::core::fmt::Formatter::write_str(f, "Coroutine"),
CaptureKind::CoroutineClosure =>
::core::fmt::Formatter::write_str(f, "CoroutineClosure"),
CaptureKind::None => ::core::fmt::Formatter::write_str(f, "None"),
}
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for CaptureKind {
#[inline]
fn eq(&self, other: &CaptureKind) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(CaptureKind::Closure(__self_0),
CaptureKind::Closure(__arg1_0)) => __self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CaptureKind {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<ty::ClosureKind>;
}
}Eq)]
32enum CaptureKind {
33 Closure(ty::ClosureKind),
34 Coroutine,
35 CoroutineClosure,
36 None,
37}
38
39#[derive(#[automatically_derived]
impl ::core::marker::Copy for Access { }Copy, #[automatically_derived]
impl ::core::clone::Clone for Access {
#[inline]
fn clone(&self) -> Access {
let _: ::core::clone::AssertParamIsClone<AccessKind>;
let _: ::core::clone::AssertParamIsClone<bool>;
*self
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for Access {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "Access",
"kind", &self.kind, "live", &self.live, "is_direct",
&&self.is_direct)
}
}Debug)]
40struct Access {
41 kind: AccessKind,
43 live: bool,
47 is_direct: bool,
50}
51
52x;#[tracing::instrument(level = "debug", skip(tcx), ret)]
53pub(crate) fn check_liveness<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> DenseBitSet<FieldIdx> {
54 if tcx.is_synthetic_mir(def_id) {
56 return DenseBitSet::new_empty(0);
57 }
58
59 if tcx.intrinsic(def_id.to_def_id()).is_some() {
61 return DenseBitSet::new_empty(0);
62 }
63
64 if find_attr!(tcx, def_id.to_def_id(), Naked(..)) {
66 return DenseBitSet::new_empty(0);
67 }
68
69 let parent = tcx.local_parent(tcx.typeck_root_def_id_local(def_id));
71 if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent)
72 && find_attr!(tcx, parent, AutomaticallyDerived(..))
73 {
74 return DenseBitSet::new_empty(0);
75 }
76
77 let mut body = &*tcx.mir_promoted(def_id).0.borrow();
78 let mut body_mem;
79
80 if body.tainted_by_errors.is_some() {
82 return DenseBitSet::new_empty(0);
83 }
84
85 let mut checked_places = PlaceSet::default();
86 checked_places.insert_locals(&body.local_decls);
87
88 let (capture_kind, num_captures) = if tcx.is_closure_like(def_id.to_def_id()) {
90 let mut self_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
91 let mut self_is_ref = false;
92 if let ty::Ref(_, ty, _) = self_ty.kind() {
93 self_ty = *ty;
94 self_is_ref = true;
95 }
96
97 let (capture_kind, args) = match self_ty.kind() {
98 ty::Closure(_, args) => {
99 (CaptureKind::Closure(args.as_closure().kind()), ty::UpvarArgs::Closure(args))
100 }
101 &ty::Coroutine(_, args) => (CaptureKind::Coroutine, ty::UpvarArgs::Coroutine(args)),
102 &ty::CoroutineClosure(_, args) => {
103 (CaptureKind::CoroutineClosure, ty::UpvarArgs::CoroutineClosure(args))
104 }
105 _ => bug!("expected closure or generator, found {:?}", self_ty),
106 };
107
108 let captures = tcx.closure_captures(def_id);
109 checked_places.insert_captures(tcx, self_is_ref, captures, args.upvar_tys());
110
111 if let CaptureKind::Closure(ty::ClosureKind::FnMut) = capture_kind {
115 body_mem = body.clone();
117 for bbdata in body_mem.basic_blocks_mut() {
118 if let TerminatorKind::Return | TerminatorKind::UnwindResume =
120 bbdata.terminator().kind
121 {
122 bbdata.terminator_mut().kind = TerminatorKind::Goto { target: START_BLOCK };
123 }
124 }
125 body = &body_mem;
126 }
127
128 (capture_kind, args.upvar_tys().len())
129 } else {
130 (CaptureKind::None, 0)
131 };
132
133 checked_places.record_debuginfo(&body.var_debug_info);
135
136 let self_assignment = find_self_assignments(&checked_places, body);
137
138 let mut live =
139 MaybeLivePlaces { tcx, capture_kind, checked_places: &checked_places, self_assignment }
140 .iterate_to_fixpoint(tcx, body, None)
141 .into_results_cursor(body);
142
143 let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id());
144
145 let mut assignments =
146 AssignmentResult::find_dead_assignments(tcx, typing_env, &checked_places, &mut live, body);
147
148 assignments.merge_guards();
149
150 let dead_captures = assignments.compute_dead_captures(num_captures);
151
152 assignments.report_fully_unused();
153 assignments.report_unused_assignments();
154
155 dead_captures
156}
157
158#[inline]
160fn is_capture(place: PlaceRef<'_>) -> bool {
161 if !place.projection.is_empty() {
162 if true {
match (&place.local, &ty::CAPTURE_STRUCT_LOCAL) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(place.local, ty::CAPTURE_STRUCT_LOCAL);
163 true
164 } else {
165 false
166 }
167}
168
169fn maybe_suggest_unit_pattern_typo<'tcx>(
171 tcx: TyCtxt<'tcx>,
172 body_def_id: DefId,
173 name: Symbol,
174 span: Span,
175 ty: Ty<'tcx>,
176) -> Option<errors::PatternTypo> {
177 if let ty::Adt(adt_def, _) = ty.peel_refs().kind() {
178 let variant_names: Vec<_> = adt_def
179 .variants()
180 .iter()
181 .filter(|v| #[allow(non_exhaustive_omitted_patterns)] match v.ctor {
Some((CtorKind::Const, _)) => true,
_ => false,
}matches!(v.ctor, Some((CtorKind::Const, _))))
182 .map(|v| v.name)
183 .collect();
184 if let Some(name) = find_best_match_for_name(&variant_names, name, None)
185 && let Some(variant) = adt_def
186 .variants()
187 .iter()
188 .find(|v| v.name == name && #[allow(non_exhaustive_omitted_patterns)] match v.ctor {
Some((CtorKind::Const, _)) => true,
_ => false,
}matches!(v.ctor, Some((CtorKind::Const, _))))
189 {
190 return Some(errors::PatternTypo {
191 span,
192 code: { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(variant.def_id) }with_no_trimmed_paths!(tcx.def_path_str(variant.def_id)),
193 kind: tcx.def_descr(variant.def_id),
194 item_name: variant.name,
195 });
196 }
197 }
198
199 let constants = tcx
202 .hir_body_owners()
203 .filter(|&def_id| {
204 #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(def_id) {
DefKind::Const { .. } => true,
_ => false,
}matches!(tcx.def_kind(def_id), DefKind::Const { .. })
205 && tcx.type_of(def_id).instantiate_identity().skip_norm_wip() == ty
206 && tcx.visibility(def_id).is_accessible_from(body_def_id, tcx)
207 })
208 .collect::<Vec<_>>();
209 let names = constants.iter().map(|&def_id| tcx.item_name(def_id)).collect::<Vec<_>>();
210 if let Some(item_name) = find_best_match_for_name(&names, name, None)
211 && let Some(position) = names.iter().position(|&n| n == item_name)
212 && let Some(&def_id) = constants.get(position)
213 {
214 return Some(errors::PatternTypo {
215 span,
216 code: { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(def_id) }with_no_trimmed_paths!(tcx.def_path_str(def_id)),
217 kind: "constant",
218 item_name,
219 });
220 }
221
222 None
223}
224
225fn maybe_drop_guard<'tcx>(
227 tcx: TyCtxt<'tcx>,
228 typing_env: ty::TypingEnv<'tcx>,
229 index: PlaceIndex,
230 ever_dropped: &DenseBitSet<PlaceIndex>,
231 checked_places: &PlaceSet<'tcx>,
232 body: &Body<'tcx>,
233) -> bool {
234 if ever_dropped.contains(index) {
235 let ty = checked_places.places[index].ty(&body.local_decls, tcx).ty;
236 #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Closure(..) | ty::Coroutine(..) | ty::Tuple(..) | ty::Adt(..) |
ty::Dynamic(..) | ty::Array(..) | ty::Slice(..) |
ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. }) => true,
_ => false,
}matches!(
237 ty.kind(),
238 ty::Closure(..)
239 | ty::Coroutine(..)
240 | ty::Tuple(..)
241 | ty::Adt(..)
242 | ty::Dynamic(..)
243 | ty::Array(..)
244 | ty::Slice(..)
245 | ty::Alias(ty::AliasTy { kind: ty::Opaque { .. }, .. })
246 ) && ty.needs_drop(tcx, typing_env)
247 } else {
248 false
249 }
250}
251
252fn annotate_mut_binding_to_immutable_binding<'tcx>(
271 tcx: TyCtxt<'tcx>,
272 place: PlaceRef<'tcx>,
273 body_def_id: LocalDefId,
274 assignment_span: Span,
275 body: &Body<'tcx>,
276) -> Option<errors::UnusedAssignSuggestion> {
277 use rustc_hir as hir;
278 use rustc_hir::intravisit::{self, Visitor};
279
280 let local = place.as_local()?;
282 let LocalKind::Arg = body.local_kind(local) else { return None };
283 let Mutability::Mut = body.local_decls[local].mutability else { return None };
284
285 let hir_param_index =
287 local.as_usize() - if tcx.is_closure_like(body_def_id.to_def_id()) { 2 } else { 1 };
288 let fn_decl = tcx.hir_node_by_def_id(body_def_id).fn_decl()?;
289 let ty = fn_decl.inputs[hir_param_index];
290 let hir::TyKind::Ref(lt, mut_ty) = ty.kind else { return None };
291
292 let hir_body = tcx.hir_maybe_body_owned_by(body_def_id)?;
294 let param = hir_body.params[hir_param_index];
295 let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = param.pat.kind else {
296 return None;
297 };
298
299 let mut finder = ExprFinder { assignment_span, lhs: None, rhs: None };
301 finder.visit_body(hir_body);
302 let lhs = finder.lhs?;
303 let rhs = finder.rhs?;
304
305 let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _mut, inner) = rhs.kind else { return None };
306
307 let pre = if lt.ident.span.is_empty() { "" } else { " " };
309 let ty_span = if mut_ty.mutbl.is_mut() {
310 None
312 } else {
313 Some(mut_ty.ty.span.shrink_to_lo())
315 };
316
317 return Some(errors::UnusedAssignSuggestion {
318 ty_span,
319 pre,
320 ty_ref_span: param.pat.span.until(ident.span),
322 pre_lhs_span: lhs.span.shrink_to_lo(),
324 rhs_borrow_span: rhs.span.until(inner.span),
326 });
327
328 #[derive(#[automatically_derived]
impl<'hir> ::core::fmt::Debug for ExprFinder<'hir> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "ExprFinder",
"assignment_span", &self.assignment_span, "lhs", &self.lhs, "rhs",
&&self.rhs)
}
}Debug)]
329 struct ExprFinder<'hir> {
330 assignment_span: Span,
331 lhs: Option<&'hir hir::Expr<'hir>>,
332 rhs: Option<&'hir hir::Expr<'hir>>,
333 }
334 impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
335 fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
336 if expr.span == self.assignment_span
337 && let hir::ExprKind::Assign(lhs, rhs, _) = expr.kind
338 {
339 self.lhs = Some(lhs);
340 self.rhs = Some(rhs);
341 } else {
342 intravisit::walk_expr(self, expr)
343 }
344 }
345 }
346}
347
348fn find_self_assignments<'tcx>(
360 checked_places: &PlaceSet<'tcx>,
361 body: &Body<'tcx>,
362) -> FxHashSet<Location> {
363 let mut self_assign = FxHashSet::default();
364
365 const FIELD_0: FieldIdx = FieldIdx::from_u32(0);
366 const FIELD_1: FieldIdx = FieldIdx::from_u32(1);
367
368 for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
369 for (statement_index, stmt) in bb_data.statements.iter().enumerate() {
370 let StatementKind::Assign(box (first_place, rvalue)) = &stmt.kind else { continue };
371 match rvalue {
372 Rvalue::BinaryOp(
374 BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow,
375 box (Operand::Copy(lhs), _),
376 ) => {
377 if statement_index + 1 != bb_data.statements.len() {
379 continue;
380 }
381
382 let TerminatorKind::Assert {
383 cond,
384 target,
385 msg: box AssertKind::Overflow(..),
386 ..
387 } = &bb_data.terminator().kind
388 else {
389 continue;
390 };
391 let Some(assign) = body.basic_blocks[*target].statements.first() else {
392 continue;
393 };
394 let StatementKind::Assign(box (dest, Rvalue::Use(Operand::Move(temp), _))) =
395 assign.kind
396 else {
397 continue;
398 };
399
400 if dest != *lhs {
401 continue;
402 }
403
404 let Operand::Move(cond) = cond else { continue };
405 let [PlaceElem::Field(FIELD_0, _)] = &temp.projection.as_slice() else {
406 continue;
407 };
408 let [PlaceElem::Field(FIELD_1, _)] = &cond.projection.as_slice() else {
409 continue;
410 };
411
412 let is_indirect = checked_places
414 .get(dest.as_ref())
415 .map_or(false, |(_, projections)| is_indirect(projections));
416 if is_indirect {
417 continue;
418 }
419
420 if first_place.local == temp.local
421 && first_place.local == cond.local
422 && first_place.projection.is_empty()
423 {
424 self_assign.insert(Location {
426 block: bb,
427 statement_index: bb_data.statements.len() - 1,
428 });
429 self_assign.insert(Location {
430 block: bb,
431 statement_index: bb_data.statements.len(),
432 });
433 self_assign.insert(Location { block: *target, statement_index: 0 });
435 }
436 }
437 Rvalue::BinaryOp(op, box (Operand::Copy(lhs), _)) => {
439 if lhs != first_place {
440 continue;
441 }
442
443 let is_indirect = checked_places
445 .get(first_place.as_ref())
446 .map_or(false, |(_, projections)| is_indirect(projections));
447 if is_indirect {
448 continue;
449 }
450
451 self_assign.insert(Location { block: bb, statement_index });
452
453 if let BinOp::Div | BinOp::Rem = op
456 && statement_index == 0
457 && let &[pred] = body.basic_blocks.predecessors()[bb].as_slice()
458 && let TerminatorKind::Assert { msg, .. } =
459 &body.basic_blocks[pred].terminator().kind
460 && let AssertKind::Overflow(..) = **msg
461 && let len = body.basic_blocks[pred].statements.len()
462 && len >= 2
463 {
464 self_assign.insert(Location { block: pred, statement_index: len - 1 });
466 self_assign.insert(Location { block: pred, statement_index: len - 2 });
468 }
469 }
470 _ => {}
471 }
472 }
473 }
474
475 self_assign
476}
477
478#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for PlaceSet<'tcx> {
#[inline]
fn default() -> PlaceSet<'tcx> {
PlaceSet {
places: ::core::default::Default::default(),
names: ::core::default::Default::default(),
locals: ::core::default::Default::default(),
capture_field_pos: ::core::default::Default::default(),
captures: ::core::default::Default::default(),
}
}
}Default, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for PlaceSet<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field5_finish(f, "PlaceSet",
"places", &self.places, "names", &self.names, "locals",
&self.locals, "capture_field_pos", &self.capture_field_pos,
"captures", &&self.captures)
}
}Debug)]
479struct PlaceSet<'tcx> {
480 places: IndexVec<PlaceIndex, PlaceRef<'tcx>>,
481 names: IndexVec<PlaceIndex, Option<(Symbol, Span)>>,
482
483 locals: IndexVec<Local, Option<PlaceIndex>>,
485
486 capture_field_pos: usize,
489 captures: IndexVec<FieldIdx, (PlaceIndex, bool)>,
491}
492
493impl<'tcx> PlaceSet<'tcx> {
494 fn insert_locals(&mut self, decls: &IndexVec<Local, LocalDecl<'tcx>>) {
495 self.locals = IndexVec::from_elem(None, &decls);
496 for (local, decl) in decls.iter_enumerated() {
497 if let LocalInfo::User(BindingForm::Var(_) | BindingForm::RefForGuard(_)) =
500 decl.local_info()
501 {
502 let index = self.places.push(local.into());
503 self.locals[local] = Some(index);
504 let _index = self.names.push(None);
505 if true {
match (&index, &_index) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(index, _index);
506 }
507 }
508 }
509
510 fn insert_captures(
511 &mut self,
512 tcx: TyCtxt<'tcx>,
513 self_is_ref: bool,
514 captures: &[&'tcx ty::CapturedPlace<'tcx>],
515 upvars: &ty::List<Ty<'tcx>>,
516 ) {
517 if true {
match (&self.locals[ty::CAPTURE_STRUCT_LOCAL], &None) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(self.locals[ty::CAPTURE_STRUCT_LOCAL], None);
519
520 let self_place = Place {
521 local: ty::CAPTURE_STRUCT_LOCAL,
522 projection: tcx.mk_place_elems(if self_is_ref { &[PlaceElem::Deref] } else { &[] }),
523 };
524 if self_is_ref {
525 self.capture_field_pos = 1;
526 }
527
528 for (f, (capture, ty)) in std::iter::zip(captures, upvars).enumerate() {
529 let f = FieldIdx::from_usize(f);
530 let elem = PlaceElem::Field(f, ty);
531 let by_ref = #[allow(non_exhaustive_omitted_patterns)] match capture.info.capture_kind {
ty::UpvarCapture::ByRef(..) => true,
_ => false,
}matches!(capture.info.capture_kind, ty::UpvarCapture::ByRef(..));
532 let place = if by_ref {
533 self_place.project_deeper(&[elem, PlaceElem::Deref], tcx)
534 } else {
535 self_place.project_deeper(&[elem], tcx)
536 };
537 let index = self.places.push(place.as_ref());
538 let _f = self.captures.push((index, by_ref));
539 if true {
match (&_f, &f) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(_f, f);
540
541 self.names.insert(
544 index,
545 (Symbol::intern(&capture.to_string(tcx)), capture.get_path_span(tcx)),
546 );
547 }
548 }
549
550 fn record_debuginfo(&mut self, var_debug_info: &Vec<VarDebugInfo<'tcx>>) {
551 let ignore_name = |name: Symbol| {
552 name == sym::empty || name == kw::SelfLower || name.as_str().starts_with('_')
553 };
554 for var_debug_info in var_debug_info {
555 if let VarDebugInfoContents::Place(place) = var_debug_info.value
556 && let Some(index) = self.locals[place.local]
557 && !ignore_name(var_debug_info.name)
558 {
559 self.names.get_or_insert_with(index, || {
560 (var_debug_info.name, var_debug_info.source_info.span)
561 });
562 }
563 }
564
565 for index_opt in self.locals.iter_mut() {
567 if let Some(index) = *index_opt {
568 let remove = match self.names[index] {
569 None => true,
570 Some((name, _)) => ignore_name(name),
571 };
572 if remove {
573 *index_opt = None;
574 }
575 }
576 }
577 }
578
579 #[inline]
580 fn get(&self, place: PlaceRef<'tcx>) -> Option<(PlaceIndex, &'tcx [PlaceElem<'tcx>])> {
581 if let Some(index) = self.locals[place.local] {
582 return Some((index, place.projection));
583 }
584 if place.local == ty::CAPTURE_STRUCT_LOCAL
585 && !self.captures.is_empty()
586 && self.capture_field_pos < place.projection.len()
587 && let PlaceElem::Field(f, _) = place.projection[self.capture_field_pos]
588 && let Some((index, by_ref)) = self.captures.get(f)
589 {
590 let mut start = self.capture_field_pos + 1;
591 if *by_ref {
592 start += 1;
594 }
595 if start <= place.projection.len() {
597 let projection = &place.projection[start..];
598 return Some((*index, projection));
599 }
600 }
601 None
602 }
603
604 fn iter(&self) -> impl Iterator<Item = (PlaceIndex, &PlaceRef<'tcx>)> {
605 self.places.iter_enumerated()
606 }
607
608 fn len(&self) -> usize {
609 self.places.len()
610 }
611}
612
613struct AssignmentResult<'a, 'tcx> {
614 tcx: TyCtxt<'tcx>,
615 typing_env: ty::TypingEnv<'tcx>,
616 checked_places: &'a PlaceSet<'tcx>,
617 body: &'a Body<'tcx>,
618 ever_live: DenseBitSet<PlaceIndex>,
620 ever_dropped: DenseBitSet<PlaceIndex>,
623 assignments: IndexVec<PlaceIndex, FxIndexMap<SourceInfo, Access>>,
630}
631
632impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
633 fn find_dead_assignments(
638 tcx: TyCtxt<'tcx>,
639 typing_env: ty::TypingEnv<'tcx>,
640 checked_places: &'a PlaceSet<'tcx>,
641 cursor: &mut ResultsCursor<'_, 'tcx, MaybeLivePlaces<'_, 'tcx>>,
642 body: &'a Body<'tcx>,
643 ) -> AssignmentResult<'a, 'tcx> {
644 let mut ever_live = DenseBitSet::new_empty(checked_places.len());
645 let mut ever_dropped = DenseBitSet::new_empty(checked_places.len());
646 let mut assignments = IndexVec::<PlaceIndex, FxIndexMap<_, _>>::from_elem(
647 Default::default(),
648 &checked_places.places,
649 );
650
651 let mut check_place =
652 |place: Place<'tcx>, kind, source_info: SourceInfo, live: &DenseBitSet<PlaceIndex>| {
653 if let Some((index, extra_projections)) = checked_places.get(place.as_ref()) {
654 if !is_indirect(extra_projections) {
655 let is_direct = extra_projections.is_empty();
656 match assignments[index].entry(source_info) {
657 IndexEntry::Vacant(v) => {
658 let access = Access { kind, live: live.contains(index), is_direct };
659 v.insert(access);
660 }
661 IndexEntry::Occupied(mut o) => {
662 o.get_mut().live |= live.contains(index);
665 o.get_mut().is_direct &= is_direct;
666 }
667 }
668 }
669 }
670 };
671
672 let mut record_drop = |place: Place<'tcx>| {
673 if let Some((index, &[])) = checked_places.get(place.as_ref()) {
674 ever_dropped.insert(index);
675 }
676 };
677
678 for (bb, bb_data) in traversal::postorder(body) {
679 cursor.seek_to_block_end(bb);
680 let live = cursor.get();
681 ever_live.union(live);
682
683 let terminator = bb_data.terminator();
684 match &terminator.kind {
685 TerminatorKind::Call { destination: place, .. }
686 | TerminatorKind::Yield { resume_arg: place, .. } => {
687 check_place(*place, AccessKind::Assign, terminator.source_info, live);
688 record_drop(*place)
689 }
690 TerminatorKind::Drop { place, .. } => record_drop(*place),
691 TerminatorKind::InlineAsm { operands, .. } => {
692 for operand in operands {
693 if let InlineAsmOperand::Out { place: Some(place), .. }
694 | InlineAsmOperand::InOut { out_place: Some(place), .. } = operand
695 {
696 check_place(*place, AccessKind::Assign, terminator.source_info, live);
697 }
698 }
699 }
700 _ => {}
701 }
702
703 for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
704 cursor.seek_before_primary_effect(Location { block: bb, statement_index });
705 let live = cursor.get();
706 ever_live.union(live);
707 match &statement.kind {
708 StatementKind::Assign(box (place, _))
709 | StatementKind::SetDiscriminant { box place, .. } => {
710 check_place(*place, AccessKind::Assign, statement.source_info, live);
711 }
712 StatementKind::StorageLive(_)
713 | StatementKind::StorageDead(_)
714 | StatementKind::Coverage(_)
715 | StatementKind::Intrinsic(_)
716 | StatementKind::Nop
717 | StatementKind::FakeRead(_)
718 | StatementKind::PlaceMention(_)
719 | StatementKind::ConstEvalCounter
720 | StatementKind::BackwardIncompatibleDropHint { .. }
721 | StatementKind::AscribeUserType(_, _) => (),
722 }
723 }
724 }
725
726 {
728 cursor.seek_to_block_start(START_BLOCK);
729 let live = cursor.get();
730 ever_live.union(live);
731
732 for (index, place) in checked_places.iter() {
734 let kind = if is_capture(*place) {
735 if place.projection.last() == Some(&PlaceElem::Deref) {
738 continue;
739 }
740
741 AccessKind::Capture
742 } else if body.local_kind(place.local) == LocalKind::Arg {
743 AccessKind::Param
744 } else {
745 continue;
746 };
747 let source_info = body.local_decls[place.local].source_info;
748 let access = Access { kind, live: live.contains(index), is_direct: true };
749 assignments[index].insert(source_info, access);
750 }
751 }
752
753 AssignmentResult {
754 tcx,
755 typing_env,
756 checked_places,
757 ever_live,
758 ever_dropped,
759 assignments,
760 body,
761 }
762 }
763
764 fn merge_guards(&mut self) {
776 for (index, place) in self.checked_places.iter() {
777 let local = place.local;
778 if let &LocalInfo::User(BindingForm::RefForGuard(arm_local)) =
779 self.body.local_decls[local].local_info()
780 {
781 if true {
if !place.projection.is_empty() {
::core::panicking::panic("assertion failed: place.projection.is_empty()")
};
};debug_assert!(place.projection.is_empty());
782
783 let Some((arm_index, _proj)) = self.checked_places.get(arm_local.into()) else {
785 continue;
786 };
787 if true {
match (&index, &arm_index) {
(left_val, right_val) => {
if *left_val == *right_val {
let kind = ::core::panicking::AssertKind::Ne;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_ne!(index, arm_index);
788 if true {
match (&_proj, &&[]) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(_proj, &[]);
789
790 if self.ever_live.contains(index) {
792 self.ever_live.insert(arm_index);
793 }
794
795 let guard_assignments = std::mem::take(&mut self.assignments[index]);
803 let arm_assignments = &mut self.assignments[arm_index];
804 for (source_info, access) in guard_assignments {
805 match arm_assignments.entry(source_info) {
806 IndexEntry::Vacant(v) => {
807 v.insert(access);
808 }
809 IndexEntry::Occupied(mut o) => {
810 o.get_mut().live |= access.live;
811 }
812 }
813 }
814 }
815 }
816 }
817
818 fn compute_dead_captures(&self, num_captures: usize) -> DenseBitSet<FieldIdx> {
820 let mut dead_captures = DenseBitSet::new_empty(num_captures);
822 for (index, place) in self.checked_places.iter() {
823 if self.ever_live.contains(index) {
824 continue;
825 }
826
827 if is_capture(*place) {
829 for p in place.projection {
830 if let PlaceElem::Field(f, _) = p {
831 dead_captures.insert(*f);
832 break;
833 }
834 }
835 continue;
836 }
837 }
838
839 dead_captures
840 }
841
842 fn is_local_in_reachable_code(&self, local: Local) -> bool {
845 struct LocalVisitor {
846 target_local: Local,
847 found: bool,
848 }
849
850 impl<'tcx> Visitor<'tcx> for LocalVisitor {
851 fn visit_local(&mut self, local: Local, _context: PlaceContext, _location: Location) {
852 if local == self.target_local {
853 self.found = true;
854 }
855 }
856 }
857
858 let mut visitor = LocalVisitor { target_local: local, found: false };
859 for (bb, bb_data) in traversal::postorder(self.body) {
860 visitor.visit_basic_block_data(bb, bb_data);
861 if visitor.found {
862 return true;
863 }
864 }
865
866 false
867 }
868
869 fn report_fully_unused(&mut self) {
871 let tcx = self.tcx;
872
873 let mut string_constants_in_body = None;
876 let mut maybe_suggest_literal_matching_name = |name: Symbol| {
877 let string_constants_in_body = string_constants_in_body.get_or_insert_with(|| {
879 struct LiteralFinder {
880 found: Vec<(Span, String)>,
881 }
882
883 impl<'tcx> Visitor<'tcx> for LiteralFinder {
884 fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
885 if let ty::Ref(_, ref_ty, _) = constant.ty().kind()
886 && ref_ty.kind() == &ty::Str
887 {
888 let rendered_constant = constant.const_.to_string();
889 self.found.push((constant.span, rendered_constant));
890 }
891 }
892 }
893
894 let mut finder = LiteralFinder { found: ::alloc::vec::Vec::new()vec![] };
895 finder.visit_body(self.body);
896 finder.found
897 });
898
899 let brace_name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{{0}", name))
})format!("{{{name}");
900 string_constants_in_body
901 .iter()
902 .filter(|(_, rendered_constant)| {
903 rendered_constant
904 .split(&brace_name)
905 .any(|c| #[allow(non_exhaustive_omitted_patterns)] match c.chars().next() {
Some('}' | ':') => true,
_ => false,
}matches!(c.chars().next(), Some('}' | ':')))
906 })
907 .map(|&(lit, _)| errors::UnusedVariableStringInterp { lit })
908 .collect::<Vec<_>>()
909 };
910
911 for (index, place) in self.checked_places.iter() {
913 if self.ever_live.contains(index) {
914 continue;
915 }
916
917 if is_capture(*place) {
919 continue;
920 }
921
922 let local = place.local;
923 let decl = &self.body.local_decls[local];
924
925 if decl.from_compiler_desugaring() {
926 continue;
927 }
928
929 let LocalInfo::User(BindingForm::Var(binding)) = decl.local_info() else { continue };
931 let Some(hir_id) = decl.source_info.scope.lint_root(&self.body.source_scopes) else {
932 continue;
933 };
934
935 let introductions = &binding.introductions;
936
937 let Some((name, def_span)) = self.checked_places.names[index] else { continue };
938
939 let from_macro = def_span.from_expansion()
942 && introductions.iter().any(|intro| intro.span.eq_ctxt(def_span));
943
944 let maybe_suggest_typo = || {
945 if let LocalKind::Arg = self.body.local_kind(local) {
946 None
947 } else {
948 maybe_suggest_unit_pattern_typo(
949 tcx,
950 self.body.source.def_id(),
951 name,
952 def_span,
953 decl.ty,
954 )
955 }
956 };
957
958 let statements = &mut self.assignments[index];
959 if statements.is_empty() {
960 if !self.is_local_in_reachable_code(local) {
961 continue;
962 }
963
964 let sugg = if from_macro {
965 errors::UnusedVariableSugg::NoSugg { span: def_span, name }
966 } else {
967 let typo = maybe_suggest_typo();
968 errors::UnusedVariableSugg::TryPrefix { spans: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[def_span]))vec![def_span], name, typo }
969 };
970 tcx.emit_node_span_lint(
971 lint::builtin::UNUSED_VARIABLES,
972 hir_id,
973 def_span,
974 errors::UnusedVariable {
975 name,
976 string_interp: maybe_suggest_literal_matching_name(name),
977 sugg,
978 },
979 );
980 continue;
981 }
982
983 statements.retain(|source_info, _| {
987 !binding.introductions.iter().any(|intro| intro.span == source_info.span)
988 });
989
990 if let Some((_, initializer_span)) = binding.opt_match_place {
993 statements.retain(|source_info, _| {
994 let within = source_info.span.find_ancestor_inside(initializer_span);
995 let outer_initializer_span =
996 initializer_span.find_ancestor_in_same_ctxt(source_info.span);
997 within.is_none()
998 && outer_initializer_span.map_or(true, |s| !s.contains(source_info.span))
999 });
1000 }
1001
1002 if !statements.is_empty() {
1003 if maybe_drop_guard(
1006 tcx,
1007 self.typing_env,
1008 index,
1009 &self.ever_dropped,
1010 self.checked_places,
1011 self.body,
1012 ) {
1013 statements.retain(|_, access| access.is_direct);
1014 if statements.is_empty() {
1015 continue;
1016 }
1017 }
1018
1019 let typo = maybe_suggest_typo();
1020 tcx.emit_node_span_lint(
1021 lint::builtin::UNUSED_VARIABLES,
1022 hir_id,
1023 def_span,
1024 errors::UnusedVarAssignedOnly { name, typo },
1025 );
1026 continue;
1027 }
1028
1029 let spans = introductions.iter().map(|intro| intro.span).collect::<Vec<_>>();
1031
1032 let any_shorthand = introductions.iter().any(|intro| intro.is_shorthand);
1033
1034 let sugg = if any_shorthand {
1035 errors::UnusedVariableSugg::TryIgnore {
1036 name,
1037 shorthands: introductions
1038 .iter()
1039 .filter_map(
1040 |intro| if intro.is_shorthand { Some(intro.span) } else { None },
1041 )
1042 .collect(),
1043 non_shorthands: introductions
1044 .iter()
1045 .filter_map(
1046 |intro| {
1047 if !intro.is_shorthand { Some(intro.span) } else { None }
1048 },
1049 )
1050 .collect(),
1051 }
1052 } else if from_macro {
1053 errors::UnusedVariableSugg::NoSugg { span: def_span, name }
1054 } else if !introductions.is_empty() {
1055 let typo = maybe_suggest_typo();
1056 errors::UnusedVariableSugg::TryPrefix { name, typo, spans: spans.clone() }
1057 } else {
1058 let typo = maybe_suggest_typo();
1059 errors::UnusedVariableSugg::TryPrefix { name, typo, spans: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[def_span]))vec![def_span] }
1060 };
1061
1062 tcx.emit_node_span_lint(
1063 lint::builtin::UNUSED_VARIABLES,
1064 hir_id,
1065 spans,
1066 errors::UnusedVariable {
1067 name,
1068 string_interp: maybe_suggest_literal_matching_name(name),
1069 sugg,
1070 },
1071 );
1072 }
1073 }
1074
1075 fn report_unused_assignments(self) {
1078 let tcx = self.tcx;
1079
1080 for (index, statements) in self.assignments.into_iter_enumerated() {
1081 if statements.is_empty() {
1082 continue;
1083 }
1084
1085 let Some((name, decl_span)) = self.checked_places.names[index] else { continue };
1086
1087 let is_maybe_drop_guard = maybe_drop_guard(
1088 tcx,
1089 self.typing_env,
1090 index,
1091 &self.ever_dropped,
1092 self.checked_places,
1093 self.body,
1094 );
1095
1096 if name.as_str().starts_with('_') {
1098 continue;
1099 }
1100
1101 let mut next_direct_assign = None;
1102 let mut dead_statements = Vec::with_capacity(statements.len());
1103
1104 for (source_info, Access { live, kind, is_direct }) in statements.into_iter() {
1105 let overwrite = match (kind, is_direct, next_direct_assign) {
1106 (AccessKind::Assign, true, Some(overwrite_span)) => {
1107 Some(errors::UnusedAssignOverwrite {
1108 assigned_span: source_info.span,
1109 overwrite_span,
1110 name,
1111 })
1112 }
1113 _ => None,
1114 };
1115
1116 if kind == AccessKind::Assign && is_direct {
1117 next_direct_assign = Some(source_info.span);
1118 }
1119
1120 if live {
1121 continue;
1122 }
1123 if !is_direct && is_maybe_drop_guard {
1126 continue;
1127 }
1128 dead_statements.push((source_info, kind, is_direct, overwrite));
1129 }
1130
1131 for (source_info, kind, is_direct, overwrite) in dead_statements.into_iter().rev() {
1134 let Some(hir_id) = source_info.scope.lint_root(&self.body.source_scopes) else {
1136 continue;
1137 };
1138
1139 match kind {
1140 AccessKind::Assign => {
1141 let suggestion = annotate_mut_binding_to_immutable_binding(
1142 tcx,
1143 self.checked_places.places[index],
1144 self.body.source.def_id().expect_local(),
1145 source_info.span,
1146 self.body,
1147 );
1148 let overwrite =
1149 if suggestion.is_none() && is_direct { overwrite } else { None };
1150 let help = suggestion.is_none() && overwrite.is_none();
1151 tcx.emit_node_span_lint(
1152 lint::builtin::UNUSED_ASSIGNMENTS,
1153 hir_id,
1154 source_info.span,
1155 errors::UnusedAssign { name, overwrite, help, suggestion },
1156 )
1157 }
1158 AccessKind::Param => tcx.emit_node_span_lint(
1159 lint::builtin::UNUSED_ASSIGNMENTS,
1160 hir_id,
1161 source_info.span,
1162 errors::UnusedAssignPassed { name },
1163 ),
1164 AccessKind::Capture => tcx.emit_node_span_lint(
1165 lint::builtin::UNUSED_ASSIGNMENTS,
1166 hir_id,
1167 decl_span,
1168 errors::UnusedCaptureMaybeCaptureRef { name },
1169 ),
1170 }
1171 }
1172 }
1173 }
1174}
1175
1176impl ::std::fmt::Debug for PlaceIndex {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fmt.write_fmt(format_args!("{0}", self.as_u32()))
}
}rustc_index::newtype_index! {
1177 pub struct PlaceIndex {}
1178}
1179
1180impl DebugWithContext<MaybeLivePlaces<'_, '_>> for PlaceIndex {
1181 fn fmt_with(
1182 &self,
1183 ctxt: &MaybeLivePlaces<'_, '_>,
1184 f: &mut std::fmt::Formatter<'_>,
1185 ) -> std::fmt::Result {
1186 std::fmt::Debug::fmt(&ctxt.checked_places.places[*self], f)
1187 }
1188}
1189
1190pub struct MaybeLivePlaces<'a, 'tcx> {
1191 tcx: TyCtxt<'tcx>,
1192 checked_places: &'a PlaceSet<'tcx>,
1193 capture_kind: CaptureKind,
1194 self_assignment: FxHashSet<Location>,
1195}
1196
1197impl<'tcx> MaybeLivePlaces<'_, 'tcx> {
1198 fn transfer_function<'a>(
1199 &'a self,
1200 trans: &'a mut DenseBitSet<PlaceIndex>,
1201 ) -> TransferFunction<'a, 'tcx> {
1202 TransferFunction {
1203 tcx: self.tcx,
1204 checked_places: &self.checked_places,
1205 capture_kind: self.capture_kind,
1206 trans,
1207 self_assignment: &self.self_assignment,
1208 }
1209 }
1210}
1211
1212impl<'tcx> Analysis<'tcx> for MaybeLivePlaces<'_, 'tcx> {
1213 type Domain = DenseBitSet<PlaceIndex>;
1214 type Direction = Backward;
1215
1216 const NAME: &'static str = "liveness-lint";
1217
1218 fn bottom_value(&self, _: &Body<'tcx>) -> Self::Domain {
1219 DenseBitSet::new_empty(self.checked_places.len())
1221 }
1222
1223 fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) {
1224 }
1226
1227 fn apply_primary_statement_effect(
1228 &self,
1229 trans: &mut Self::Domain,
1230 statement: &Statement<'tcx>,
1231 location: Location,
1232 ) {
1233 self.transfer_function(trans).visit_statement(statement, location);
1234 }
1235
1236 fn apply_primary_terminator_effect<'mir>(
1237 &self,
1238 trans: &mut Self::Domain,
1239 terminator: &'mir Terminator<'tcx>,
1240 location: Location,
1241 ) -> TerminatorEdges<'mir, 'tcx> {
1242 self.transfer_function(trans).visit_terminator(terminator, location);
1243 terminator.edges()
1244 }
1245
1246 fn apply_call_return_effect(
1247 &self,
1248 _trans: &mut Self::Domain,
1249 _block: BasicBlock,
1250 _return_places: CallReturnPlaces<'_, 'tcx>,
1251 ) {
1252 }
1254}
1255
1256struct TransferFunction<'a, 'tcx> {
1257 tcx: TyCtxt<'tcx>,
1258 checked_places: &'a PlaceSet<'tcx>,
1259 trans: &'a mut DenseBitSet<PlaceIndex>,
1260 capture_kind: CaptureKind,
1261 self_assignment: &'a FxHashSet<Location>,
1262}
1263
1264impl<'tcx> Visitor<'tcx> for TransferFunction<'_, 'tcx> {
1265 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1266 match statement.kind {
1267 StatementKind::FakeRead(box (
1270 FakeReadCause::ForLet(None) | FakeReadCause::ForGuardBinding,
1271 _,
1272 )) => return,
1273 StatementKind::Assign(box (ref dest, ref rvalue))
1275 if self.self_assignment.contains(&location) =>
1276 {
1277 if let Rvalue::BinaryOp(
1278 BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow,
1279 box (_, rhs),
1280 ) = rvalue
1281 {
1282 self.visit_operand(rhs, location);
1286 self.visit_place(
1287 dest,
1288 PlaceContext::MutatingUse(MutatingUseContext::Store),
1289 location,
1290 );
1291 } else if let Rvalue::BinaryOp(_, box (_, rhs)) = rvalue {
1292 self.visit_operand(rhs, location);
1296 } else {
1297 self.visit_rvalue(rvalue, location);
1302 }
1303 }
1304 _ => self.super_statement(statement, location),
1305 }
1306 }
1307
1308 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
1309 match terminator.kind {
1312 TerminatorKind::Return
1313 | TerminatorKind::Yield { .. }
1314 | TerminatorKind::Goto { target: START_BLOCK } | TerminatorKind::Call { target: None, .. } if self.capture_kind != CaptureKind::None =>
1317 {
1318 for (index, place) in self.checked_places.iter() {
1320 if place.local == ty::CAPTURE_STRUCT_LOCAL
1321 && place.projection.last() == Some(&PlaceElem::Deref)
1322 {
1323 self.trans.insert(index);
1324 }
1325 }
1326 }
1327 TerminatorKind::Drop { .. } => {}
1329 TerminatorKind::Assert { .. } => {}
1331 _ => self.super_terminator(terminator, location),
1332 }
1333 }
1334
1335 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1336 match rvalue {
1337 Rvalue::Aggregate(
1341 box AggregateKind::Closure(def_id, _) | box AggregateKind::Coroutine(def_id, _),
1342 operands,
1343 ) => {
1344 if let Some(def_id) = def_id.as_local() {
1345 let dead_captures = self.tcx.check_liveness(def_id);
1346 for (field, operand) in
1347 operands.iter_enumerated().take(dead_captures.domain_size())
1348 {
1349 if !dead_captures.contains(field) {
1350 self.visit_operand(operand, location);
1351 }
1352 }
1353 }
1354 }
1355 _ => self.super_rvalue(rvalue, location),
1356 }
1357 }
1358
1359 fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
1360 if let Some((index, extra_projections)) = self.checked_places.get(place.as_ref()) {
1361 for i in (extra_projections.len()..=place.projection.len()).rev() {
1362 let place_part =
1363 PlaceRef { local: place.local, projection: &place.projection[..i] };
1364 let extra_projections = &place.projection[i..];
1365
1366 if let Some(&elem) = extra_projections.get(0) {
1367 self.visit_projection_elem(place_part, elem, context, location);
1368 }
1369 }
1370
1371 match DefUse::for_place(extra_projections, context) {
1372 Some(DefUse::Def) => {
1373 self.trans.remove(index);
1374 }
1375 Some(DefUse::Use) => {
1376 self.trans.insert(index);
1377 }
1378 None => {}
1379 }
1380 } else {
1381 self.super_place(place, context, location)
1382 }
1383 }
1384
1385 fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
1386 if let Some((index, _proj)) = self.checked_places.get(local.into()) {
1387 if true {
match (&_proj, &&[]) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(_proj, &[]);
1388 match DefUse::for_place(&[], context) {
1389 Some(DefUse::Def) => {
1390 self.trans.remove(index);
1391 }
1392 Some(DefUse::Use) => {
1393 self.trans.insert(index);
1394 }
1395 _ => {}
1396 }
1397 }
1398 }
1399}
1400
1401#[derive(#[automatically_derived]
impl ::core::cmp::Eq for DefUse {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for DefUse {
#[inline]
fn eq(&self, other: &DefUse) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::fmt::Debug for DefUse {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self { DefUse::Def => "Def", DefUse::Use => "Use", })
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for DefUse {
#[inline]
fn clone(&self) -> DefUse {
match self { DefUse::Def => DefUse::Def, DefUse::Use => DefUse::Use, }
}
}Clone)]
1402enum DefUse {
1403 Def,
1404 Use,
1405}
1406
1407fn is_indirect(proj: &[PlaceElem<'_>]) -> bool {
1408 proj.iter().any(|p| p.is_indirect())
1409}
1410
1411impl DefUse {
1412 fn for_place<'tcx>(projection: &[PlaceElem<'tcx>], context: PlaceContext) -> Option<DefUse> {
1413 let is_indirect = is_indirect(projection);
1414 match context {
1415 PlaceContext::MutatingUse(
1416 MutatingUseContext::Store | MutatingUseContext::SetDiscriminant,
1417 ) => {
1418 if is_indirect {
1419 Some(DefUse::Use)
1422 } else if projection.is_empty() {
1423 Some(DefUse::Def)
1424 } else {
1425 None
1426 }
1427 }
1428
1429 PlaceContext::MutatingUse(
1434 MutatingUseContext::Call
1435 | MutatingUseContext::Yield
1436 | MutatingUseContext::AsmOutput,
1437 ) => is_indirect.then_some(DefUse::Use),
1438
1439 PlaceContext::MutatingUse(
1441 MutatingUseContext::RawBorrow
1442 | MutatingUseContext::Borrow
1443 | MutatingUseContext::Drop
1444 | MutatingUseContext::Retag,
1445 )
1446 | PlaceContext::NonMutatingUse(
1447 NonMutatingUseContext::RawBorrow
1448 | NonMutatingUseContext::Copy
1449 | NonMutatingUseContext::Inspect
1450 | NonMutatingUseContext::Move
1451 | NonMutatingUseContext::FakeBorrow
1452 | NonMutatingUseContext::SharedBorrow
1453 | NonMutatingUseContext::PlaceMention,
1454 ) => Some(DefUse::Use),
1455
1456 PlaceContext::NonUse(
1457 NonUseContext::StorageLive
1458 | NonUseContext::StorageDead
1459 | NonUseContext::AscribeUserTy(_)
1460 | NonUseContext::BackwardIncompatibleDropHint
1461 | NonUseContext::VarDebugInfo,
1462 ) => None,
1463
1464 PlaceContext::MutatingUse(MutatingUseContext::Projection)
1465 | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
1466 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("A projection could be a def or a use and must be handled separately")));
}unreachable!("A projection could be a def or a use and must be handled separately")
1467 }
1468 }
1469 }
1470}