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::diagnostics;
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<Location>;
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_field4_finish(f, "Access",
"kind", &self.kind, "location", &self.location, "live",
&self.live, "is_direct", &&self.is_direct)
}
}Debug)]
40struct Access {
41 kind: AccessKind,
43 location: Location,
45 live: bool,
49 is_direct: bool,
52}
53
54x;#[tracing::instrument(level = "debug", skip(tcx), ret)]
55pub(crate) fn check_liveness<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> DenseBitSet<FieldIdx> {
56 if tcx.is_synthetic_mir(def_id) {
58 return DenseBitSet::new_empty(0);
59 }
60
61 if tcx.intrinsic(def_id.to_def_id()).is_some() {
63 return DenseBitSet::new_empty(0);
64 }
65
66 if find_attr!(tcx, def_id.to_def_id(), Naked(..)) {
68 return DenseBitSet::new_empty(0);
69 }
70
71 let parent = tcx.local_parent(tcx.typeck_root_def_id_local(def_id));
73 if let DefKind::Impl { of_trait: true } = tcx.def_kind(parent)
74 && find_attr!(tcx, parent, AutomaticallyDerived)
75 {
76 return DenseBitSet::new_empty(0);
77 }
78
79 let mut body = &*tcx.mir_promoted(def_id).0.borrow();
80 let mut body_mem;
81
82 if body.tainted_by_errors.is_some() {
84 return DenseBitSet::new_empty(0);
85 }
86
87 let mut checked_places = PlaceSet::default();
88 checked_places.insert_locals(&body.local_decls);
89
90 let (capture_kind, num_captures) = if tcx.is_closure_like(def_id.to_def_id()) {
92 let mut self_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
93 let mut self_is_ref = false;
94 if let ty::Ref(_, ty, _) = self_ty.kind() {
95 self_ty = *ty;
96 self_is_ref = true;
97 }
98
99 let (capture_kind, args) = match self_ty.kind() {
100 ty::Closure(_, args) => {
101 (CaptureKind::Closure(args.as_closure().kind()), ty::UpvarArgs::Closure(args))
102 }
103 &ty::Coroutine(_, args) => (CaptureKind::Coroutine, ty::UpvarArgs::Coroutine(args)),
104 &ty::CoroutineClosure(_, args) => {
105 (CaptureKind::CoroutineClosure, ty::UpvarArgs::CoroutineClosure(args))
106 }
107 _ => bug!("expected closure or generator, found {:?}", self_ty),
108 };
109
110 let captures = tcx.closure_captures(def_id);
111 checked_places.insert_captures(tcx, self_is_ref, captures, args.upvar_tys());
112
113 if let CaptureKind::Closure(ty::ClosureKind::FnMut) = capture_kind {
117 body_mem = body.clone();
119 for bbdata in body_mem.basic_blocks_mut() {
120 if let TerminatorKind::Return | TerminatorKind::UnwindResume =
122 bbdata.terminator().kind
123 {
124 bbdata.terminator_mut().kind = TerminatorKind::Goto { target: START_BLOCK };
125 }
126 }
127 body = &body_mem;
128 }
129
130 (capture_kind, args.upvar_tys().len())
131 } else {
132 (CaptureKind::None, 0)
133 };
134
135 checked_places.record_debuginfo(&body.var_debug_info);
137
138 let self_assignment = find_self_assignments(&checked_places, body);
139
140 let mut live =
141 MaybeLivePlaces { tcx, capture_kind, checked_places: &checked_places, self_assignment }
142 .iterate_to_fixpoint(tcx, body, None)
143 .into_results_cursor(body);
144
145 let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id());
146
147 let mut assignments =
148 AssignmentResult::find_dead_assignments(tcx, typing_env, &checked_places, &mut live, body);
149
150 assignments.merge_guards();
151
152 let dead_captures = assignments.compute_dead_captures(num_captures);
153
154 assignments.report_fully_unused();
155 assignments.report_unused_assignments();
156
157 dead_captures
158}
159
160#[inline]
162fn is_capture(place: PlaceRef<'_>) -> bool {
163 if !place.projection.is_empty() {
164 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);
165 true
166 } else {
167 false
168 }
169}
170
171fn maybe_suggest_unit_pattern_typo<'tcx>(
173 tcx: TyCtxt<'tcx>,
174 body_def_id: DefId,
175 name: Symbol,
176 span: Span,
177 ty: Ty<'tcx>,
178) -> Option<diagnostics::PatternTypo> {
179 if let ty::Adt(adt_def, _) = ty.peel_refs().kind() {
180 let variant_names: Vec<_> = adt_def
181 .variants()
182 .iter()
183 .filter(|v| #[allow(non_exhaustive_omitted_patterns)] match v.ctor {
Some((CtorKind::Const, _)) => true,
_ => false,
}matches!(v.ctor, Some((CtorKind::Const, _))))
184 .map(|v| v.name)
185 .collect();
186 if let Some(name) = find_best_match_for_name(&variant_names, name, None)
187 && let Some(variant) = adt_def
188 .variants()
189 .iter()
190 .find(|v| v.name == name && #[allow(non_exhaustive_omitted_patterns)] match v.ctor {
Some((CtorKind::Const, _)) => true,
_ => false,
}matches!(v.ctor, Some((CtorKind::Const, _))))
191 {
192 return Some(diagnostics::PatternTypo {
193 span,
194 code: { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(variant.def_id) }with_no_trimmed_paths!(tcx.def_path_str(variant.def_id)),
195 kind: tcx.def_descr(variant.def_id),
196 item_name: variant.name,
197 });
198 }
199 }
200
201 let constants = tcx
204 .hir_body_owners()
205 .filter(|&def_id| {
206 #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(def_id) {
DefKind::Const { .. } => true,
_ => false,
}matches!(tcx.def_kind(def_id), DefKind::Const { .. })
207 && tcx.type_of(def_id).instantiate_identity().skip_norm_wip() == ty
208 && tcx.visibility(def_id).is_accessible_from(body_def_id, tcx)
209 })
210 .collect::<Vec<_>>();
211 let names = constants.iter().map(|&def_id| tcx.item_name(def_id)).collect::<Vec<_>>();
212 if let Some(item_name) = find_best_match_for_name(&names, name, None)
213 && let Some(position) = names.iter().position(|&n| n == item_name)
214 && let Some(&def_id) = constants.get(position)
215 {
216 return Some(diagnostics::PatternTypo {
217 span,
218 code: { let _guard = NoTrimmedGuard::new(); tcx.def_path_str(def_id) }with_no_trimmed_paths!(tcx.def_path_str(def_id)),
219 kind: "constant",
220 item_name,
221 });
222 }
223
224 None
225}
226
227fn maybe_drop_guard<'tcx>(
229 tcx: TyCtxt<'tcx>,
230 typing_env: ty::TypingEnv<'tcx>,
231 index: PlaceIndex,
232 ever_dropped: &DenseBitSet<PlaceIndex>,
233 checked_places: &PlaceSet<'tcx>,
234 body: &Body<'tcx>,
235) -> bool {
236 if ever_dropped.contains(index) {
237 let ty = checked_places.places[index].ty(&body.local_decls, tcx).ty;
238 let ty = ty::set_aliases_to_non_rigid(tcx, ty).skip_norm_wip();
243 #[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!(
244 ty.kind(),
245 ty::Closure(..)
246 | ty::Coroutine(..)
247 | ty::Tuple(..)
248 | ty::Adt(..)
249 | ty::Dynamic(..)
250 | ty::Array(..)
251 | ty::Slice(..)
252 | ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. })
253 ) && ty.needs_drop(tcx, typing_env)
254 } else {
255 false
256 }
257}
258
259fn annotate_mut_binding_to_immutable_binding<'tcx>(
278 tcx: TyCtxt<'tcx>,
279 place: PlaceRef<'tcx>,
280 body_def_id: LocalDefId,
281 assignment_span: Span,
282 body: &Body<'tcx>,
283) -> Option<diagnostics::UnusedAssignSuggestion> {
284 use rustc_hir as hir;
285 use rustc_hir::intravisit::{self, Visitor};
286
287 let local = place.as_local()?;
289 let LocalKind::Arg = body.local_kind(local) else { return None };
290 let Mutability::Mut = body.local_decls[local].mutability else { return None };
291
292 let hir_param_index =
294 local.as_usize() - if tcx.is_closure_like(body_def_id.to_def_id()) { 2 } else { 1 };
295 let fn_decl = tcx.hir_node_by_def_id(body_def_id).fn_decl()?;
296 let ty = fn_decl.inputs[hir_param_index];
297 let hir::TyKind::Ref(lt, mut_ty) = ty.kind else { return None };
298
299 let hir_body = tcx.hir_maybe_body_owned_by(body_def_id)?;
301 let param = hir_body.params[hir_param_index];
302 let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = param.pat.kind else {
303 return None;
304 };
305
306 let mut finder = ExprFinder { assignment_span, lhs: None, rhs: None };
308 finder.visit_body(hir_body);
309 let lhs = finder.lhs?;
310 let rhs = finder.rhs?;
311
312 let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _mut, inner) = rhs.kind else { return None };
313
314 let pre = if lt.ident.span.is_empty() { "" } else { " " };
316 let ty_span = if mut_ty.mutbl.is_mut() {
317 None
319 } else {
320 Some(mut_ty.ty.span.shrink_to_lo())
322 };
323
324 return Some(diagnostics::UnusedAssignSuggestion {
325 ty_span,
326 pre,
327 ty_ref_span: param.pat.span.until(ident.span),
329 pre_lhs_span: lhs.span.shrink_to_lo(),
331 rhs_borrow_span: rhs.span.until(inner.span),
333 });
334
335 #[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)]
336 struct ExprFinder<'hir> {
337 assignment_span: Span,
338 lhs: Option<&'hir hir::Expr<'hir>>,
339 rhs: Option<&'hir hir::Expr<'hir>>,
340 }
341 impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
342 fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
343 if expr.span == self.assignment_span
344 && let hir::ExprKind::Assign(lhs, rhs, _) = expr.kind
345 {
346 self.lhs = Some(lhs);
347 self.rhs = Some(rhs);
348 } else {
349 intravisit::walk_expr(self, expr)
350 }
351 }
352 }
353}
354
355fn find_self_assignments<'tcx>(
367 checked_places: &PlaceSet<'tcx>,
368 body: &Body<'tcx>,
369) -> FxHashSet<Location> {
370 let mut self_assign = FxHashSet::default();
371
372 const FIELD_0: FieldIdx = FieldIdx::from_u32(0);
373 const FIELD_1: FieldIdx = FieldIdx::from_u32(1);
374
375 for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
376 for (statement_index, stmt) in bb_data.statements.iter().enumerate() {
377 let StatementKind::Assign((first_place, rvalue)) = &stmt.kind else { continue };
378 match rvalue {
379 Rvalue::BinaryOp(
381 BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow,
382 (Operand::Copy(lhs), _),
383 ) => {
384 if statement_index + 1 != bb_data.statements.len() {
386 continue;
387 }
388
389 let TerminatorKind::Assert {
390 cond, target, msg: AssertKind::Overflow(..), ..
391 } = &bb_data.terminator().kind
392 else {
393 continue;
394 };
395 let Some(assign) = body.basic_blocks[*target].statements.first() else {
396 continue;
397 };
398 let StatementKind::Assign((dest, Rvalue::Use(Operand::Move(temp), _))) =
399 assign.kind
400 else {
401 continue;
402 };
403
404 if dest != *lhs {
405 continue;
406 }
407
408 let Operand::Move(cond) = cond else { continue };
409 let [PlaceElem::Field(FIELD_0, _)] = &temp.projection.as_slice() else {
410 continue;
411 };
412 let [PlaceElem::Field(FIELD_1, _)] = &cond.projection.as_slice() else {
413 continue;
414 };
415
416 let is_indirect = checked_places
418 .get(dest.as_ref())
419 .map_or(false, |(_, projections)| is_indirect(projections));
420 if is_indirect {
421 continue;
422 }
423
424 if first_place.local == temp.local
425 && first_place.local == cond.local
426 && first_place.projection.is_empty()
427 {
428 self_assign.insert(Location {
430 block: bb,
431 statement_index: bb_data.statements.len() - 1,
432 });
433 self_assign.insert(Location {
434 block: bb,
435 statement_index: bb_data.statements.len(),
436 });
437 self_assign.insert(Location { block: *target, statement_index: 0 });
439 }
440 }
441 Rvalue::BinaryOp(op, (Operand::Copy(lhs), _)) => {
443 if lhs != first_place {
444 continue;
445 }
446
447 let is_indirect = checked_places
449 .get(first_place.as_ref())
450 .map_or(false, |(_, projections)| is_indirect(projections));
451 if is_indirect {
452 continue;
453 }
454
455 self_assign.insert(Location { block: bb, statement_index });
456
457 if let BinOp::Div | BinOp::Rem = op
460 && statement_index == 0
461 && let &[pred] = body.basic_blocks.predecessors()[bb].as_slice()
462 && let TerminatorKind::Assert { msg, .. } =
463 &body.basic_blocks[pred].terminator().kind
464 && let AssertKind::Overflow(..) = **msg
465 && let len = body.basic_blocks[pred].statements.len()
466 && len >= 2
467 {
468 self_assign.insert(Location { block: pred, statement_index: len - 1 });
470 self_assign.insert(Location { block: pred, statement_index: len - 2 });
472 }
473 }
474 _ => {}
475 }
476 }
477 }
478
479 self_assign
480}
481
482#[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)]
483struct PlaceSet<'tcx> {
484 places: IndexVec<PlaceIndex, PlaceRef<'tcx>>,
485 names: IndexVec<PlaceIndex, Option<(Symbol, Span)>>,
486
487 locals: IndexVec<Local, Option<PlaceIndex>>,
489
490 capture_field_pos: usize,
493 captures: IndexVec<FieldIdx, (PlaceIndex, bool)>,
495}
496
497impl<'tcx> PlaceSet<'tcx> {
498 fn insert_locals(&mut self, decls: &IndexVec<Local, LocalDecl<'tcx>>) {
499 self.locals = IndexVec::from_elem(None, &decls);
500 for (local, decl) in decls.iter_enumerated() {
501 if let LocalInfo::User(BindingForm::Var(_) | BindingForm::RefForGuard(_)) =
504 decl.local_info()
505 {
506 let index = self.places.push(local.into());
507 self.locals[local] = Some(index);
508 let _index = self.names.push(None);
509 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);
510 }
511 }
512 }
513
514 fn insert_captures(
515 &mut self,
516 tcx: TyCtxt<'tcx>,
517 self_is_ref: bool,
518 captures: &[&'tcx ty::CapturedPlace<'tcx>],
519 upvars: &ty::List<Ty<'tcx>>,
520 ) {
521 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);
523
524 let self_place = Place {
525 local: ty::CAPTURE_STRUCT_LOCAL,
526 projection: tcx.mk_place_elems(if self_is_ref { &[PlaceElem::Deref] } else { &[] }),
527 };
528 if self_is_ref {
529 self.capture_field_pos = 1;
530 }
531
532 for (f, (capture, ty)) in std::iter::zip(captures, upvars).enumerate() {
533 let f = FieldIdx::from_usize(f);
534 let elem = PlaceElem::Field(f, ty);
535 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(..));
536 let place = if by_ref {
537 self_place.project_deeper(&[elem, PlaceElem::Deref], tcx)
538 } else {
539 self_place.project_deeper(&[elem], tcx)
540 };
541 let index = self.places.push(place.as_ref());
542 let _f = self.captures.push((index, by_ref));
543 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);
544
545 self.names.insert(
548 index,
549 (Symbol::intern(&capture.to_string(tcx)), capture.get_path_span(tcx)),
550 );
551 }
552 }
553
554 fn record_debuginfo(&mut self, var_debug_info: &Vec<VarDebugInfo<'tcx>>) {
555 let ignore_name = |name: Symbol| {
556 name == sym::empty || name == kw::SelfLower || name.as_str().starts_with('_')
557 };
558 for var_debug_info in var_debug_info {
559 if let VarDebugInfoContents::Place(place) = var_debug_info.value
560 && let Some(index) = self.locals[place.local]
561 && !ignore_name(var_debug_info.name)
562 {
563 self.names.get_or_insert_with(index, || {
564 (var_debug_info.name, var_debug_info.source_info.span)
565 });
566 }
567 }
568
569 for index_opt in self.locals.iter_mut() {
571 if let Some(index) = *index_opt {
572 let remove = match self.names[index] {
573 None => true,
574 Some((name, _)) => ignore_name(name),
575 };
576 if remove {
577 *index_opt = None;
578 }
579 }
580 }
581 }
582
583 #[inline]
584 fn get(&self, place: PlaceRef<'tcx>) -> Option<(PlaceIndex, &'tcx [PlaceElem<'tcx>])> {
585 if let Some(index) = self.locals[place.local] {
586 return Some((index, place.projection));
587 }
588 if place.local == ty::CAPTURE_STRUCT_LOCAL
589 && !self.captures.is_empty()
590 && self.capture_field_pos < place.projection.len()
591 && let PlaceElem::Field(f, _) = place.projection[self.capture_field_pos]
592 && let Some((index, by_ref)) = self.captures.get(f)
593 {
594 let mut start = self.capture_field_pos + 1;
595 if *by_ref {
596 start += 1;
598 }
599 if start <= place.projection.len() {
601 let projection = &place.projection[start..];
602 return Some((*index, projection));
603 }
604 }
605 None
606 }
607
608 fn iter(&self) -> impl Iterator<Item = (PlaceIndex, &PlaceRef<'tcx>)> {
609 self.places.iter_enumerated()
610 }
611
612 fn len(&self) -> usize {
613 self.places.len()
614 }
615}
616
617struct AssignmentResult<'a, 'tcx> {
618 tcx: TyCtxt<'tcx>,
619 typing_env: ty::TypingEnv<'tcx>,
620 checked_places: &'a PlaceSet<'tcx>,
621 body: &'a Body<'tcx>,
622 ever_live: DenseBitSet<PlaceIndex>,
624 ever_dropped: DenseBitSet<PlaceIndex>,
627 assignments: IndexVec<PlaceIndex, FxIndexMap<SourceInfo, Access>>,
634}
635
636impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
637 fn find_dead_assignments(
642 tcx: TyCtxt<'tcx>,
643 typing_env: ty::TypingEnv<'tcx>,
644 checked_places: &'a PlaceSet<'tcx>,
645 cursor: &mut ResultsCursor<'_, 'tcx, MaybeLivePlaces<'_, 'tcx>>,
646 body: &'a Body<'tcx>,
647 ) -> AssignmentResult<'a, 'tcx> {
648 let mut ever_live = DenseBitSet::new_empty(checked_places.len());
649 let mut ever_dropped = DenseBitSet::new_empty(checked_places.len());
650 let mut assignments = IndexVec::<PlaceIndex, FxIndexMap<_, _>>::from_elem(
651 Default::default(),
652 &checked_places.places,
653 );
654
655 let mut check_place = |place: Place<'tcx>,
656 kind,
657 source_info: SourceInfo,
658 location: Location,
659 live: &DenseBitSet<PlaceIndex>| {
660 if let Some((index, extra_projections)) = checked_places.get(place.as_ref()) {
661 if !is_indirect(extra_projections) {
662 let is_direct = extra_projections.is_empty();
663 match assignments[index].entry(source_info) {
664 IndexEntry::Vacant(v) => {
665 let access =
666 Access { kind, location, live: live.contains(index), is_direct };
667 v.insert(access);
668 }
669 IndexEntry::Occupied(mut o) => {
670 o.get_mut().live |= live.contains(index);
673 o.get_mut().is_direct &= is_direct;
674 }
675 }
676 }
677 }
678 };
679
680 let mut record_drop = |place: Place<'tcx>| {
681 if let Some((index, &[])) = checked_places.get(place.as_ref()) {
682 ever_dropped.insert(index);
683 }
684 };
685
686 for (bb, bb_data) in traversal::postorder(body) {
687 cursor.seek_to_block_end(bb);
688 let live = cursor.get();
689 ever_live.union(live);
690
691 let terminator = bb_data.terminator();
692 match &terminator.kind {
693 TerminatorKind::Call { destination: place, .. }
694 | TerminatorKind::Yield { resume_arg: place, .. } => {
695 check_place(
696 *place,
697 AccessKind::Assign,
698 terminator.source_info,
699 body.terminator_loc(bb),
700 live,
701 );
702 record_drop(*place)
703 }
704 TerminatorKind::Drop { place, .. } => record_drop(*place),
705 TerminatorKind::InlineAsm { operands, .. } => {
706 for operand in operands {
707 if let InlineAsmOperand::Out { place: Some(place), .. }
708 | InlineAsmOperand::InOut { out_place: Some(place), .. } = operand
709 {
710 check_place(
711 *place,
712 AccessKind::Assign,
713 terminator.source_info,
714 body.terminator_loc(bb),
715 live,
716 );
717 }
718 }
719 }
720 _ => {}
721 }
722
723 for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() {
724 let location = Location { block: bb, statement_index };
725 cursor.seek_before_primary_effect(location);
726 let live = cursor.get();
727 ever_live.union(live);
728 match &statement.kind {
729 StatementKind::Assign((place, _)) => {
730 check_place(
731 *place,
732 AccessKind::Assign,
733 statement.source_info,
734 location,
735 live,
736 );
737 }
738 StatementKind::SetDiscriminant { place, .. } => {
739 check_place(
740 **place,
741 AccessKind::Assign,
742 statement.source_info,
743 location,
744 live,
745 );
746 }
747 StatementKind::StorageLive(_)
748 | StatementKind::StorageDead(_)
749 | StatementKind::Coverage(_)
750 | StatementKind::Intrinsic(_)
751 | StatementKind::Nop
752 | StatementKind::FakeRead(_)
753 | StatementKind::PlaceMention(_)
754 | StatementKind::ConstEvalCounter
755 | StatementKind::BackwardIncompatibleDropHint { .. }
756 | StatementKind::AscribeUserType(_, _) => (),
757 }
758 }
759 }
760
761 {
763 cursor.seek_to_block_start(START_BLOCK);
764 let live = cursor.get();
765 ever_live.union(live);
766
767 for (index, place) in checked_places.iter() {
769 let kind = if is_capture(*place) {
770 if place.projection.last() == Some(&PlaceElem::Deref) {
773 continue;
774 }
775
776 AccessKind::Capture
777 } else if body.local_kind(place.local) == LocalKind::Arg {
778 AccessKind::Param
779 } else {
780 continue;
781 };
782 let source_info = body.local_decls[place.local].source_info;
783 let access = Access {
784 kind,
785 location: Location::START,
786 live: live.contains(index),
787 is_direct: true,
788 };
789 assignments[index].insert(source_info, access);
790 }
791 }
792
793 AssignmentResult {
794 tcx,
795 typing_env,
796 checked_places,
797 ever_live,
798 ever_dropped,
799 assignments,
800 body,
801 }
802 }
803
804 fn merge_guards(&mut self) {
816 for (index, place) in self.checked_places.iter() {
817 let local = place.local;
818 if let &LocalInfo::User(BindingForm::RefForGuard(arm_local)) =
819 self.body.local_decls[local].local_info()
820 {
821 if true {
if !place.projection.is_empty() {
::core::panicking::panic("assertion failed: place.projection.is_empty()")
};
};debug_assert!(place.projection.is_empty());
822
823 let Some((arm_index, _proj)) = self.checked_places.get(arm_local.into()) else {
825 continue;
826 };
827 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);
828 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, &[]);
829
830 if self.ever_live.contains(index) {
832 self.ever_live.insert(arm_index);
833 }
834
835 let guard_assignments = std::mem::take(&mut self.assignments[index]);
843 let arm_assignments = &mut self.assignments[arm_index];
844 for (source_info, access) in guard_assignments {
845 match arm_assignments.entry(source_info) {
846 IndexEntry::Vacant(v) => {
847 v.insert(access);
848 }
849 IndexEntry::Occupied(mut o) => {
850 o.get_mut().live |= access.live;
851 }
852 }
853 }
854 }
855 }
856 }
857
858 fn compute_dead_captures(&self, num_captures: usize) -> DenseBitSet<FieldIdx> {
860 let mut dead_captures = DenseBitSet::new_empty(num_captures);
862 for (index, place) in self.checked_places.iter() {
863 if self.ever_live.contains(index) {
864 continue;
865 }
866
867 if is_capture(*place) {
869 for p in place.projection {
870 if let PlaceElem::Field(f, _) = p {
871 dead_captures.insert(*f);
872 break;
873 }
874 }
875 continue;
876 }
877 }
878
879 dead_captures
880 }
881
882 fn is_local_in_reachable_code(&self, local: Local) -> bool {
885 struct LocalVisitor {
886 target_local: Local,
887 found: bool,
888 }
889
890 impl<'tcx> Visitor<'tcx> for LocalVisitor {
891 fn visit_local(&mut self, local: Local, _context: PlaceContext, _location: Location) {
892 if local == self.target_local {
893 self.found = true;
894 }
895 }
896 }
897
898 let mut visitor = LocalVisitor { target_local: local, found: false };
899 for (bb, bb_data) in traversal::postorder(self.body) {
900 visitor.visit_basic_block_data(bb, bb_data);
901 if visitor.found {
902 return true;
903 }
904 }
905
906 false
907 }
908
909 fn is_local_used_in_source(&self, name: Symbol, def_span: Span) -> bool {
920 use rustc_hir as hir;
921 use rustc_hir::def::Res;
922 use rustc_hir::intravisit::{self, Visitor};
923
924 let Some(body_def_id) = self.body.source.def_id().as_local() else { return false };
925 let Some(hir_body) = self.tcx.hir_maybe_body_owned_by(body_def_id) else { return false };
926 let typeck_results = self.tcx.typeck(body_def_id);
927
928 struct LocalUseVisitor<'a, 'tcx> {
929 tcx: TyCtxt<'tcx>,
930 typeck_results: &'a ty::TypeckResults<'tcx>,
931 name: Symbol,
932 def_span: Span,
933 found: bool,
934 }
935
936 impl<'a, 'tcx> Visitor<'tcx> for LocalUseVisitor<'a, 'tcx> {
937 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
938 if self.found {
939 return;
940 }
941
942 if let hir::ExprKind::Path(qpath) = &expr.kind
943 && let Res::Local(hir_id) = self.typeck_results.qpath_res(qpath, expr.hir_id)
944 && self.tcx.hir_name(hir_id) == self.name
945 && self.tcx.hir_span(hir_id) == self.def_span
946 {
947 self.found = true;
948 return;
949 }
950
951 intravisit::walk_expr(self, expr);
952 }
953 }
954
955 let mut visitor =
956 LocalUseVisitor { tcx: self.tcx, typeck_results, name, def_span, found: false };
957 visitor.visit_body(hir_body);
958 visitor.found
959 }
960
961 fn report_fully_unused(&mut self) {
963 let tcx = self.tcx;
964
965 let mut string_constants_in_body = None;
968 let mut maybe_suggest_literal_matching_name = |name: Symbol| {
969 let string_constants_in_body = string_constants_in_body.get_or_insert_with(|| {
971 struct LiteralFinder {
972 found: Vec<(Span, String)>,
973 }
974
975 impl<'tcx> Visitor<'tcx> for LiteralFinder {
976 fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
977 if let ty::Ref(_, ref_ty, _) = constant.ty().kind()
978 && ref_ty.kind() == &ty::Str
979 {
980 let rendered_constant = constant.const_.to_string();
981 self.found.push((constant.span, rendered_constant));
982 }
983 }
984 }
985
986 let mut finder = LiteralFinder { found: ::alloc::vec::Vec::new()vec![] };
987 finder.visit_body(self.body);
988 finder.found
989 });
990
991 let brace_name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{{0}", name))
})format!("{{{name}");
992 string_constants_in_body
993 .iter()
994 .filter(|(_, rendered_constant)| {
995 rendered_constant
996 .split(&brace_name)
997 .any(|c| #[allow(non_exhaustive_omitted_patterns)] match c.chars().next() {
Some('}' | ':') => true,
_ => false,
}matches!(c.chars().next(), Some('}' | ':')))
998 })
999 .map(|&(lit, _)| diagnostics::UnusedVariableStringInterp { lit })
1000 .collect::<Vec<_>>()
1001 };
1002
1003 for (index, place) in self.checked_places.iter() {
1005 if self.ever_live.contains(index) {
1006 continue;
1007 }
1008
1009 if is_capture(*place) {
1011 continue;
1012 }
1013
1014 let local = place.local;
1015 let decl = &self.body.local_decls[local];
1016
1017 if decl.from_compiler_desugaring() {
1018 continue;
1019 }
1020
1021 let LocalInfo::User(BindingForm::Var(binding)) = decl.local_info() else { continue };
1023 let Some(hir_id) = decl.source_info.scope.lint_root(&self.body.source_scopes) else {
1024 continue;
1025 };
1026
1027 let introductions = &binding.introductions;
1028
1029 let Some((name, def_span)) = self.checked_places.names[index] else { continue };
1030
1031 let from_macro = def_span.from_expansion()
1034 && introductions.iter().any(|intro| intro.span.eq_ctxt(def_span));
1035
1036 let maybe_suggest_typo = || {
1037 if let LocalKind::Arg = self.body.local_kind(local) {
1038 None
1039 } else {
1040 maybe_suggest_unit_pattern_typo(
1041 tcx,
1042 self.body.source.def_id(),
1043 name,
1044 def_span,
1045 decl.ty,
1046 )
1047 }
1048 };
1049
1050 let is_used_after_uninitialized = self.body.local_kind(local) == LocalKind::Temp
1053 && #[allow(non_exhaustive_omitted_patterns)] match binding.opt_match_place {
Some((None, _)) => true,
_ => false,
}matches!(binding.opt_match_place, Some((None, _)))
1054 && self.is_local_used_in_source(name, def_span);
1055
1056 let statements = &mut self.assignments[index];
1057 if statements.is_empty() {
1058 if is_used_after_uninitialized {
1059 continue;
1064 }
1065
1066 if !self.is_local_in_reachable_code(local) {
1067 continue;
1068 }
1069
1070 let sugg = if from_macro {
1071 diagnostics::UnusedVariableSugg::NoSugg { span: def_span, name }
1072 } else {
1073 let typo = maybe_suggest_typo();
1074 diagnostics::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 }
1075 };
1076 tcx.emit_node_span_lint(
1077 lint::builtin::UNUSED_VARIABLES,
1078 hir_id,
1079 def_span,
1080 diagnostics::UnusedVariable {
1081 name,
1082 string_interp: maybe_suggest_literal_matching_name(name),
1083 sugg,
1084 },
1085 );
1086 continue;
1087 }
1088
1089 statements.retain(|source_info, _| {
1093 !binding.introductions.iter().any(|intro| intro.span == source_info.span)
1094 });
1095
1096 if let Some((_, initializer_span)) = binding.opt_match_place {
1099 statements.retain(|source_info, _| {
1100 let within = source_info.span.find_ancestor_inside(initializer_span);
1101 let outer_initializer_span =
1102 initializer_span.find_ancestor_in_same_ctxt(source_info.span);
1103 within.is_none()
1104 && outer_initializer_span.map_or(true, |s| !s.contains(source_info.span))
1105 });
1106 }
1107
1108 if !statements.is_empty() {
1109 if maybe_drop_guard(
1112 tcx,
1113 self.typing_env,
1114 index,
1115 &self.ever_dropped,
1116 self.checked_places,
1117 self.body,
1118 ) {
1119 statements.retain(|_, access| access.is_direct);
1120 if statements.is_empty() {
1121 continue;
1122 }
1123 }
1124
1125 let typo = maybe_suggest_typo();
1126 tcx.emit_node_span_lint(
1127 lint::builtin::UNUSED_VARIABLES,
1128 hir_id,
1129 def_span,
1130 diagnostics::UnusedVarAssignedOnly { name, typo },
1131 );
1132 continue;
1133 }
1134
1135 let spans = introductions.iter().map(|intro| intro.span).collect::<Vec<_>>();
1137
1138 let any_shorthand = introductions.iter().any(|intro| intro.is_shorthand);
1139
1140 let sugg = if any_shorthand {
1141 diagnostics::UnusedVariableSugg::TryIgnore {
1142 name: name.to_ident_string(),
1143 shorthands: introductions
1144 .iter()
1145 .filter_map(
1146 |intro| if intro.is_shorthand { Some(intro.span) } else { None },
1147 )
1148 .collect(),
1149 non_shorthands: introductions
1150 .iter()
1151 .filter_map(
1152 |intro| {
1153 if !intro.is_shorthand { Some(intro.span) } else { None }
1154 },
1155 )
1156 .collect(),
1157 }
1158 } else if from_macro {
1159 diagnostics::UnusedVariableSugg::NoSugg { span: def_span, name }
1160 } else if !introductions.is_empty() {
1161 let typo = maybe_suggest_typo();
1162 diagnostics::UnusedVariableSugg::TryPrefix { name, typo, spans: spans.clone() }
1163 } else {
1164 let typo = maybe_suggest_typo();
1165 diagnostics::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] }
1166 };
1167
1168 tcx.emit_node_span_lint(
1169 lint::builtin::UNUSED_VARIABLES,
1170 hir_id,
1171 spans,
1172 diagnostics::UnusedVariable {
1173 name,
1174 string_interp: maybe_suggest_literal_matching_name(name),
1175 sugg,
1176 },
1177 );
1178 }
1179 }
1180
1181 fn report_unused_assignments(self) {
1184 let tcx = self.tcx;
1185
1186 for (index, statements) in self.assignments.into_iter_enumerated() {
1187 if statements.is_empty() {
1188 continue;
1189 }
1190
1191 let Some((name, decl_span)) = self.checked_places.names[index] else { continue };
1192
1193 let is_maybe_drop_guard = maybe_drop_guard(
1194 tcx,
1195 self.typing_env,
1196 index,
1197 &self.ever_dropped,
1198 self.checked_places,
1199 self.body,
1200 );
1201
1202 if name.as_str().starts_with('_') {
1204 continue;
1205 }
1206
1207 let mut next_direct_assignments: Vec<(Span, Location)> = Vec::new();
1208 let mut dead_statements = Vec::with_capacity(statements.len());
1209
1210 for (source_info, Access { live, kind, is_direct, location }) in statements.into_iter()
1211 {
1212 let direct_assignment = kind == AccessKind::Assign && is_direct;
1213 let should_report = !live && (is_direct || !is_maybe_drop_guard);
1214
1215 let overwrite = if should_report && direct_assignment {
1216 next_direct_assignments
1217 .iter()
1218 .rfind(|(_, overwrite_location)| {
1219 location.is_predecessor_of(*overwrite_location, self.body)
1220 })
1221 .map(|&(overwrite_span, _)| diagnostics::UnusedAssignOverwrite {
1222 assigned_span: source_info.span,
1223 overwrite_span,
1224 name,
1225 })
1226 } else {
1227 None
1228 };
1229
1230 if direct_assignment {
1231 next_direct_assignments.push((source_info.span, location));
1232 }
1233
1234 if !should_report {
1235 continue;
1236 }
1237 dead_statements.push((source_info, kind, is_direct, overwrite));
1238 }
1239
1240 for (source_info, kind, is_direct, overwrite) in dead_statements.into_iter().rev() {
1243 let Some(hir_id) = source_info.scope.lint_root(&self.body.source_scopes) else {
1245 continue;
1246 };
1247
1248 match kind {
1249 AccessKind::Assign => {
1250 let suggestion = annotate_mut_binding_to_immutable_binding(
1251 tcx,
1252 self.checked_places.places[index],
1253 self.body.source.def_id().expect_local(),
1254 source_info.span,
1255 self.body,
1256 );
1257 let overwrite =
1258 if suggestion.is_none() && is_direct { overwrite } else { None };
1259 let help = suggestion.is_none() && overwrite.is_none();
1260 tcx.emit_node_span_lint(
1261 lint::builtin::UNUSED_ASSIGNMENTS,
1262 hir_id,
1263 source_info.span,
1264 diagnostics::UnusedAssign { name, overwrite, help, suggestion },
1265 )
1266 }
1267 AccessKind::Param => tcx.emit_node_span_lint(
1268 lint::builtin::UNUSED_ASSIGNMENTS,
1269 hir_id,
1270 source_info.span,
1271 diagnostics::UnusedAssignPassed { name },
1272 ),
1273 AccessKind::Capture => tcx.emit_node_span_lint(
1274 lint::builtin::UNUSED_ASSIGNMENTS,
1275 hir_id,
1276 decl_span,
1277 diagnostics::UnusedCaptureMaybeCaptureRef { name },
1278 ),
1279 }
1280 }
1281 }
1282 }
1283}
1284
1285impl ::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! {
1286 pub struct PlaceIndex {}
1287}
1288
1289impl DebugWithContext<MaybeLivePlaces<'_, '_>> for PlaceIndex {
1290 fn fmt_with(
1291 &self,
1292 ctxt: &MaybeLivePlaces<'_, '_>,
1293 f: &mut std::fmt::Formatter<'_>,
1294 ) -> std::fmt::Result {
1295 std::fmt::Debug::fmt(&ctxt.checked_places.places[*self], f)
1296 }
1297}
1298
1299pub struct MaybeLivePlaces<'a, 'tcx> {
1300 tcx: TyCtxt<'tcx>,
1301 checked_places: &'a PlaceSet<'tcx>,
1302 capture_kind: CaptureKind,
1303 self_assignment: FxHashSet<Location>,
1304}
1305
1306impl<'tcx> MaybeLivePlaces<'_, 'tcx> {
1307 fn transfer_function<'a>(
1308 &'a self,
1309 trans: &'a mut DenseBitSet<PlaceIndex>,
1310 ) -> TransferFunction<'a, 'tcx> {
1311 TransferFunction {
1312 tcx: self.tcx,
1313 checked_places: &self.checked_places,
1314 capture_kind: self.capture_kind,
1315 trans,
1316 self_assignment: &self.self_assignment,
1317 }
1318 }
1319}
1320
1321impl<'tcx> Analysis<'tcx> for MaybeLivePlaces<'_, 'tcx> {
1322 type Domain = DenseBitSet<PlaceIndex>;
1323 type Direction = Backward;
1324
1325 const NAME: &'static str = "liveness-lint";
1326
1327 fn bottom_value(&self, _: &Body<'tcx>) -> Self::Domain {
1328 DenseBitSet::new_empty(self.checked_places.len())
1330 }
1331
1332 fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) {
1333 }
1335
1336 fn apply_primary_statement_effect(
1337 &self,
1338 trans: &mut Self::Domain,
1339 statement: &Statement<'tcx>,
1340 location: Location,
1341 ) {
1342 self.transfer_function(trans).visit_statement(statement, location);
1343 }
1344
1345 fn apply_primary_terminator_effect<'mir>(
1346 &self,
1347 trans: &mut Self::Domain,
1348 terminator: &'mir Terminator<'tcx>,
1349 location: Location,
1350 ) -> TerminatorEdges<'mir, 'tcx> {
1351 self.transfer_function(trans).visit_terminator(terminator, location);
1352 terminator.edges()
1353 }
1354
1355 fn apply_call_return_effect(
1356 &self,
1357 _trans: &mut Self::Domain,
1358 _block: BasicBlock,
1359 _return_places: CallReturnPlaces<'_, 'tcx>,
1360 ) {
1361 }
1363}
1364
1365struct TransferFunction<'a, 'tcx> {
1366 tcx: TyCtxt<'tcx>,
1367 checked_places: &'a PlaceSet<'tcx>,
1368 trans: &'a mut DenseBitSet<PlaceIndex>,
1369 capture_kind: CaptureKind,
1370 self_assignment: &'a FxHashSet<Location>,
1371}
1372
1373impl<'tcx> Visitor<'tcx> for TransferFunction<'_, 'tcx> {
1374 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1375 match statement.kind {
1376 StatementKind::FakeRead((
1379 FakeReadCause::ForLet(None) | FakeReadCause::ForGuardBinding,
1380 _,
1381 )) => return,
1382 StatementKind::Assign((ref dest, ref rvalue))
1384 if self.self_assignment.contains(&location) =>
1385 {
1386 if let Rvalue::BinaryOp(
1387 BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow,
1388 (_, rhs),
1389 ) = rvalue
1390 {
1391 self.visit_operand(rhs, location);
1395 self.visit_place(
1396 dest,
1397 PlaceContext::MutatingUse(MutatingUseContext::Store),
1398 location,
1399 );
1400 } else if let Rvalue::BinaryOp(_, (_, rhs)) = rvalue {
1401 self.visit_operand(rhs, location);
1405 } else {
1406 self.visit_rvalue(rvalue, location);
1411 }
1412 }
1413 _ => self.super_statement(statement, location),
1414 }
1415 }
1416
1417 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
1418 match terminator.kind {
1421 TerminatorKind::Return
1422 | TerminatorKind::Yield { .. }
1423 | TerminatorKind::Goto { target: START_BLOCK } | TerminatorKind::Call { target: None, .. } if self.capture_kind != CaptureKind::None =>
1426 {
1427 for (index, place) in self.checked_places.iter() {
1429 if place.local == ty::CAPTURE_STRUCT_LOCAL
1430 && place.projection.last() == Some(&PlaceElem::Deref)
1431 {
1432 self.trans.insert(index);
1433 }
1434 }
1435 }
1436 TerminatorKind::Drop { .. } => {}
1438 TerminatorKind::Assert { .. } => {}
1440 _ => self.super_terminator(terminator, location),
1441 }
1442 }
1443
1444 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1445 match rvalue {
1446 Rvalue::Aggregate(
1450 AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _),
1451 operands,
1452 ) => {
1453 if let Some(def_id) = def_id.as_local() {
1454 let dead_captures = self.tcx.check_liveness(def_id);
1455 for (field, operand) in
1456 operands.iter_enumerated().take(dead_captures.domain_size())
1457 {
1458 if !dead_captures.contains(field) {
1459 self.visit_operand(operand, location);
1460 }
1461 }
1462 }
1463 }
1464 _ => self.super_rvalue(rvalue, location),
1465 }
1466 }
1467
1468 fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
1469 if let Some((index, extra_projections)) = self.checked_places.get(place.as_ref()) {
1470 for i in (extra_projections.len()..=place.projection.len()).rev() {
1471 let place_part =
1472 PlaceRef { local: place.local, projection: &place.projection[..i] };
1473 let extra_projections = &place.projection[i..];
1474
1475 if let Some(&elem) = extra_projections.get(0) {
1476 self.visit_projection_elem(place_part, elem, context, location);
1477 }
1478 }
1479
1480 match DefUse::for_place(extra_projections, context) {
1481 Some(DefUse::Def) => {
1482 self.trans.remove(index);
1483 }
1484 Some(DefUse::Use) => {
1485 self.trans.insert(index);
1486 }
1487 None => {}
1488 }
1489 } else {
1490 self.super_place(place, context, location)
1491 }
1492 }
1493
1494 fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
1495 if let Some((index, _proj)) = self.checked_places.get(local.into()) {
1496 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, &[]);
1497 match DefUse::for_place(&[], context) {
1498 Some(DefUse::Def) => {
1499 self.trans.remove(index);
1500 }
1501 Some(DefUse::Use) => {
1502 self.trans.insert(index);
1503 }
1504 _ => {}
1505 }
1506 }
1507 }
1508}
1509
1510#[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)]
1511enum DefUse {
1512 Def,
1513 Use,
1514}
1515
1516fn is_indirect(proj: &[PlaceElem<'_>]) -> bool {
1517 proj.iter().any(|p| p.is_indirect())
1518}
1519
1520impl DefUse {
1521 fn for_place<'tcx>(projection: &[PlaceElem<'tcx>], context: PlaceContext) -> Option<DefUse> {
1522 let is_indirect = is_indirect(projection);
1523 match context {
1524 PlaceContext::MutatingUse(
1525 MutatingUseContext::Store | MutatingUseContext::SetDiscriminant,
1526 ) => {
1527 if is_indirect {
1528 Some(DefUse::Use)
1531 } else if projection.is_empty() {
1532 Some(DefUse::Def)
1533 } else {
1534 None
1535 }
1536 }
1537
1538 PlaceContext::MutatingUse(
1543 MutatingUseContext::Call
1544 | MutatingUseContext::Yield
1545 | MutatingUseContext::AsmOutput,
1546 ) => is_indirect.then_some(DefUse::Use),
1547
1548 PlaceContext::MutatingUse(
1550 MutatingUseContext::RawBorrow
1551 | MutatingUseContext::Borrow
1552 | MutatingUseContext::Drop
1553 | MutatingUseContext::Retag,
1554 )
1555 | PlaceContext::NonMutatingUse(
1556 NonMutatingUseContext::RawBorrow
1557 | NonMutatingUseContext::Copy
1558 | NonMutatingUseContext::Inspect
1559 | NonMutatingUseContext::Move
1560 | NonMutatingUseContext::FakeBorrow
1561 | NonMutatingUseContext::SharedBorrow
1562 | NonMutatingUseContext::PlaceMention,
1563 ) => Some(DefUse::Use),
1564
1565 PlaceContext::NonUse(
1566 NonUseContext::StorageLive
1567 | NonUseContext::StorageDead
1568 | NonUseContext::AscribeUserTy(_)
1569 | NonUseContext::BackwardIncompatibleDropHint
1570 | NonUseContext::VarDebugInfo,
1571 ) => None,
1572
1573 PlaceContext::MutatingUse(MutatingUseContext::Projection)
1574 | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
1575 {
::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")
1576 }
1577 }
1578 }
1579}