Skip to content

Commit 972a19f

Browse files
committed
Auto merge of #15659 - HKalbasi:unused-var, r=HKalbasi
Add `unused_variables` native diagnostic
2 parents 862a300 + ab52ba2 commit 972a19f

24 files changed

+404
-142
lines changed

crates/hir-ty/src/layout/tests/closure.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,9 @@ fn capture_specific_fields() {
186186
fn match_pattern() {
187187
size_and_align_expr! {
188188
struct X(i64, i32, (u8, i128));
189-
let y: X = X(2, 5, (7, 3));
189+
let _y: X = X(2, 5, (7, 3));
190190
move |x: i64| {
191-
match y {
191+
match _y {
192192
_ => x,
193193
}
194194
}

crates/hir-ty/src/mir.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ impl ProjectionId {
280280
}
281281
}
282282

283-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
283+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
284284
pub struct Place {
285285
pub local: LocalId,
286286
pub projection: ProjectionId,
@@ -1007,7 +1007,7 @@ pub enum Rvalue {
10071007
#[derive(Debug, PartialEq, Eq, Clone)]
10081008
pub enum StatementKind {
10091009
Assign(Place, Rvalue),
1010-
//FakeRead(Box<(FakeReadCause, Place)>),
1010+
FakeRead(Place),
10111011
//SetDiscriminant {
10121012
// place: Box<Place>,
10131013
// variant_index: VariantIdx,
@@ -1109,7 +1109,9 @@ impl MirBody {
11091109
}
11101110
}
11111111
}
1112-
StatementKind::Deinit(p) => f(p, &mut self.projection_store),
1112+
StatementKind::FakeRead(p) | StatementKind::Deinit(p) => {
1113+
f(p, &mut self.projection_store)
1114+
}
11131115
StatementKind::StorageLive(_)
11141116
| StatementKind::StorageDead(_)
11151117
| StatementKind::Nop => (),

crates/hir-ty/src/mir/borrowck.rs

+74-14
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use super::{
2424
pub enum MutabilityReason {
2525
Mut { spans: Vec<MirSpan> },
2626
Not,
27+
Unused,
2728
}
2829

2930
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -144,7 +145,8 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
144145
}
145146
}
146147
},
147-
StatementKind::Deinit(_)
148+
StatementKind::FakeRead(_)
149+
| StatementKind::Deinit(_)
148150
| StatementKind::StorageLive(_)
149151
| StatementKind::StorageDead(_)
150152
| StatementKind::Nop => (),
@@ -264,7 +266,10 @@ fn ever_initialized_map(
264266
is_ever_initialized = false;
265267
}
266268
}
267-
StatementKind::Deinit(_) | StatementKind::Nop | StatementKind::StorageLive(_) => (),
269+
StatementKind::Deinit(_)
270+
| StatementKind::FakeRead(_)
271+
| StatementKind::Nop
272+
| StatementKind::StorageLive(_) => (),
268273
}
269274
}
270275
let Some(terminator) = &block.terminator else {
@@ -331,16 +336,37 @@ fn ever_initialized_map(
331336
result
332337
}
333338

339+
fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) {
340+
match &mut result[local] {
341+
MutabilityReason::Mut { spans } => spans.push(span),
342+
it @ (MutabilityReason::Not | MutabilityReason::Unused) => {
343+
*it = MutabilityReason::Mut { spans: vec![span] }
344+
}
345+
};
346+
}
347+
348+
fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) {
349+
match &mut result[local] {
350+
it @ MutabilityReason::Unused => {
351+
*it = MutabilityReason::Not;
352+
}
353+
_ => (),
354+
};
355+
}
356+
357+
fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) {
358+
if let Operand::Copy(p) | Operand::Move(p) = arg {
359+
record_usage(p.local, result);
360+
}
361+
}
362+
334363
fn mutability_of_locals(
335364
db: &dyn HirDatabase,
336365
body: &MirBody,
337366
) -> ArenaMap<LocalId, MutabilityReason> {
338367
let mut result: ArenaMap<LocalId, MutabilityReason> =
339-
body.locals.iter().map(|it| (it.0, MutabilityReason::Not)).collect();
340-
let mut push_mut_span = |local, span| match &mut result[local] {
341-
MutabilityReason::Mut { spans } => spans.push(span),
342-
it @ MutabilityReason::Not => *it = MutabilityReason::Mut { spans: vec![span] },
343-
};
368+
body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect();
369+
344370
let ever_init_maps = ever_initialized_map(db, body);
345371
for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
346372
let block = &body.basic_blocks[block_id];
@@ -350,23 +376,51 @@ fn mutability_of_locals(
350376
match place_case(db, body, place) {
351377
ProjectionCase::Direct => {
352378
if ever_init_map.get(place.local).copied().unwrap_or_default() {
353-
push_mut_span(place.local, statement.span);
379+
push_mut_span(place.local, statement.span, &mut result);
354380
} else {
355381
ever_init_map.insert(place.local, true);
356382
}
357383
}
358384
ProjectionCase::DirectPart => {
359385
// Partial initialization is not supported, so it is definitely `mut`
360-
push_mut_span(place.local, statement.span);
386+
push_mut_span(place.local, statement.span, &mut result);
387+
}
388+
ProjectionCase::Indirect => {
389+
record_usage(place.local, &mut result);
361390
}
362-
ProjectionCase::Indirect => (),
391+
}
392+
match value {
393+
Rvalue::CopyForDeref(p)
394+
| Rvalue::Discriminant(p)
395+
| Rvalue::Len(p)
396+
| Rvalue::Ref(_, p) => {
397+
record_usage(p.local, &mut result);
398+
}
399+
Rvalue::Use(o)
400+
| Rvalue::Repeat(o, _)
401+
| Rvalue::Cast(_, o, _)
402+
| Rvalue::UnaryOp(_, o) => record_usage_for_operand(o, &mut result),
403+
Rvalue::CheckedBinaryOp(_, o1, o2) => {
404+
for o in [o1, o2] {
405+
record_usage_for_operand(o, &mut result);
406+
}
407+
}
408+
Rvalue::Aggregate(_, args) => {
409+
for arg in args.iter() {
410+
record_usage_for_operand(arg, &mut result);
411+
}
412+
}
413+
Rvalue::ShallowInitBox(_, _) | Rvalue::ShallowInitBoxWithAlloc(_) => (),
363414
}
364415
if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value {
365416
if place_case(db, body, p) != ProjectionCase::Indirect {
366-
push_mut_span(p.local, statement.span);
417+
push_mut_span(p.local, statement.span, &mut result);
367418
}
368419
}
369420
}
421+
StatementKind::FakeRead(p) => {
422+
record_usage(p.local, &mut result);
423+
}
370424
StatementKind::StorageDead(p) => {
371425
ever_init_map.insert(*p, false);
372426
}
@@ -386,15 +440,21 @@ fn mutability_of_locals(
386440
| TerminatorKind::FalseEdge { .. }
387441
| TerminatorKind::FalseUnwind { .. }
388442
| TerminatorKind::GeneratorDrop
389-
| TerminatorKind::SwitchInt { .. }
390443
| TerminatorKind::Drop { .. }
391444
| TerminatorKind::DropAndReplace { .. }
392445
| TerminatorKind::Assert { .. }
393446
| TerminatorKind::Yield { .. } => (),
394-
TerminatorKind::Call { destination, .. } => {
447+
TerminatorKind::SwitchInt { discr, targets: _ } => {
448+
record_usage_for_operand(discr, &mut result);
449+
}
450+
TerminatorKind::Call { destination, args, func, .. } => {
451+
record_usage_for_operand(func, &mut result);
452+
for arg in args.iter() {
453+
record_usage_for_operand(arg, &mut result);
454+
}
395455
if destination.projection.lookup(&body.projection_store).len() == 0 {
396456
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
397-
push_mut_span(destination.local, MirSpan::Unknown);
457+
push_mut_span(destination.local, MirSpan::Unknown, &mut result);
398458
} else {
399459
ever_init_map.insert(destination.local, true);
400460
}

crates/hir-ty/src/mir/eval.rs

+1
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,7 @@ impl Evaluator<'_> {
842842
}
843843
StatementKind::Deinit(_) => not_supported!("de-init statement"),
844844
StatementKind::StorageLive(_)
845+
| StatementKind::FakeRead(_)
845846
| StatementKind::StorageDead(_)
846847
| StatementKind::Nop => (),
847848
}

crates/hir-ty/src/mir/lower.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
529529
else {
530530
return Ok(None);
531531
};
532+
self.push_fake_read(current, cond_place, expr_id.into());
532533
let (then_target, else_target) =
533534
self.pattern_match(current, None, cond_place, *pat)?;
534535
self.write_bytes_to_place(
@@ -668,6 +669,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
668669
else {
669670
return Ok(None);
670671
};
672+
self.push_fake_read(current, cond_place, expr_id.into());
671673
let mut end = None;
672674
for MatchArm { pat, guard, expr } in arms.iter() {
673675
let (then, mut otherwise) =
@@ -1299,6 +1301,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
12991301
return Ok(None);
13001302
};
13011303
if matches!(&self.body.exprs[lhs], Expr::Underscore) {
1304+
self.push_fake_read_for_operand(current, rhs_op, span);
13021305
return Ok(Some(current));
13031306
}
13041307
if matches!(
@@ -1575,6 +1578,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
15751578
self.result.basic_blocks[block].statements.push(statement);
15761579
}
15771580

1581+
fn push_fake_read(&mut self, block: BasicBlockId, p: Place, span: MirSpan) {
1582+
self.push_statement(block, StatementKind::FakeRead(p).with_span(span));
1583+
}
1584+
1585+
fn push_fake_read_for_operand(&mut self, block: BasicBlockId, operand: Operand, span: MirSpan) {
1586+
if let Operand::Move(p) | Operand::Copy(p) = operand {
1587+
self.push_fake_read(block, p, span);
1588+
}
1589+
}
1590+
15781591
fn push_assignment(
15791592
&mut self,
15801593
block: BasicBlockId,
@@ -1733,6 +1746,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
17331746
return Ok(None);
17341747
};
17351748
current = c;
1749+
self.push_fake_read(current, init_place, span);
17361750
(current, else_block) =
17371751
self.pattern_match(current, None, init_place, *pat)?;
17381752
match (else_block, else_branch) {
@@ -1760,13 +1774,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
17601774
}
17611775
}
17621776
}
1763-
hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
1777+
&hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
17641778
let scope2 = self.push_drop_scope();
1765-
let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
1779+
let Some((p, c)) = self.lower_expr_as_place(current, expr, true)? else {
17661780
scope2.pop_assume_dropped(self);
17671781
scope.pop_assume_dropped(self);
17681782
return Ok(None);
17691783
};
1784+
self.push_fake_read(c, p, expr.into());
17701785
current = scope2.pop_and_drop(self, c);
17711786
}
17721787
}

crates/hir-ty/src/mir/monomorphization.rs

+1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ impl Filler<'_> {
248248
| Rvalue::CopyForDeref(_) => (),
249249
},
250250
StatementKind::Deinit(_)
251+
| StatementKind::FakeRead(_)
251252
| StatementKind::StorageLive(_)
252253
| StatementKind::StorageDead(_)
253254
| StatementKind::Nop => (),

crates/hir-ty/src/mir/pretty.rs

+5
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,11 @@ impl<'a> MirPrettyCtx<'a> {
233233
this.place(p);
234234
wln!(this, ");");
235235
}
236+
StatementKind::FakeRead(p) => {
237+
w!(this, "FakeRead(");
238+
this.place(p);
239+
wln!(this, ");");
240+
}
236241
StatementKind::Nop => wln!(this, "Nop;"),
237242
}
238243
}

crates/hir/src/diagnostics.rs

+6
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ diagnostics![
6666
UnresolvedModule,
6767
UnresolvedProcMacro,
6868
UnusedMut,
69+
UnusedVariable,
6970
];
7071

7172
#[derive(Debug)]
@@ -270,6 +271,11 @@ pub struct UnusedMut {
270271
pub local: Local,
271272
}
272273

274+
#[derive(Debug)]
275+
pub struct UnusedVariable {
276+
pub local: Local,
277+
}
278+
273279
#[derive(Debug)]
274280
pub struct MovedOutOfRef {
275281
pub ty: Type,

crates/hir/src/lib.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub use crate::{
9898
ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
9999
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
100100
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
101-
UnresolvedProcMacro, UnusedMut,
101+
UnresolvedProcMacro, UnusedMut, UnusedVariable,
102102
},
103103
has_source::HasSource,
104104
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -1697,9 +1697,20 @@ impl DefWithBody {
16971697
// Skip synthetic bindings
16981698
continue;
16991699
}
1700-
let need_mut = &mol[local];
1700+
let mut need_mut = &mol[local];
1701+
if body[binding_id].name.as_str() == Some("self")
1702+
&& need_mut == &mir::MutabilityReason::Unused
1703+
{
1704+
need_mut = &mir::MutabilityReason::Not;
1705+
}
17011706
let local = Local { parent: self.into(), binding_id };
17021707
match (need_mut, local.is_mut(db)) {
1708+
(mir::MutabilityReason::Unused, _) => {
1709+
let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with("_"));
1710+
if !should_ignore {
1711+
acc.push(UnusedVariable { local }.into())
1712+
}
1713+
}
17031714
(mir::MutabilityReason::Mut { .. }, true)
17041715
| (mir::MutabilityReason::Not, false) => (),
17051716
(mir::MutabilityReason::Mut { spans }, false) => {

crates/ide-diagnostics/src/handlers/field_shorthand.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ fn main() {
166166
check_diagnostics(
167167
r#"
168168
struct A { a: &'static str }
169-
fn f(a: A) { let A { a: hello } = a; }
169+
fn f(a: A) { let A { a: _hello } = a; }
170170
"#,
171171
);
172172
check_diagnostics(
@@ -181,12 +181,14 @@ fn f(a: A) { let A { 0: 0 } = a; }
181181
struct A { a: &'static str }
182182
fn f(a: A) {
183183
let A { a$0: a } = a;
184+
_ = a;
184185
}
185186
"#,
186187
r#"
187188
struct A { a: &'static str }
188189
fn f(a: A) {
189190
let A { a } = a;
191+
_ = a;
190192
}
191193
"#,
192194
);
@@ -196,12 +198,14 @@ fn f(a: A) {
196198
struct A { a: &'static str, b: &'static str }
197199
fn f(a: A) {
198200
let A { a$0: a, b } = a;
201+
_ = (a, b);
199202
}
200203
"#,
201204
r#"
202205
struct A { a: &'static str, b: &'static str }
203206
fn f(a: A) {
204207
let A { a, b } = a;
208+
_ = (a, b);
205209
}
206210
"#,
207211
);

0 commit comments

Comments
 (0)