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