diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index f7dc703560245..e047580d5bad6 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -836,6 +836,33 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { dead_captures } + /// Check if a local is referenced in any reachable basic block. + /// Variables in unreachable code (e.g., after `todo!()`) should not trigger unused warnings. + fn is_local_in_reachable_code(&self, local: Local) -> bool { + struct LocalVisitor { + target_local: Local, + found: bool, + } + + impl<'tcx> Visitor<'tcx> for LocalVisitor { + fn visit_local(&mut self, local: Local, _context: PlaceContext, _location: Location) { + if local == self.target_local { + self.found = true; + } + } + } + + let mut visitor = LocalVisitor { target_local: local, found: false }; + for (bb, bb_data) in traversal::postorder(self.body) { + visitor.visit_basic_block_data(bb, bb_data); + if visitor.found { + return true; + } + } + + false + } + /// Report fully unused locals, and forget the corresponding assignments. fn report_fully_unused(&mut self) { let tcx = self.tcx; @@ -927,6 +954,10 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let statements = &mut self.assignments[index]; if statements.is_empty() { + if !self.is_local_in_reachable_code(local) { + continue; + } + let sugg = if from_macro { errors::UnusedVariableSugg::NoSugg { span: def_span, name } } else { diff --git a/tests/ui/lint/unused/unused-var-in-unreachable-code.rs b/tests/ui/lint/unused/unused-var-in-unreachable-code.rs new file mode 100644 index 0000000000000..39c5e0315d9ce --- /dev/null +++ b/tests/ui/lint/unused/unused-var-in-unreachable-code.rs @@ -0,0 +1,46 @@ +//@ check-pass + +#![allow(unreachable_code)] +#![allow(dead_code)] +#![warn(unused_variables)] + +fn after_todo() { + todo!("not implemented"); + + // This should not warn - the code is unreachable + let a = 1; + if a < 2 { + eprintln!("a: {}", a); + } +} + +fn after_panic() { + panic!("oops"); + + // This should not warn - the code is unreachable + let b = 2; + println!("{}", b); +} + +fn after_unimplemented() { + unimplemented!(); + + // This should not warn - the code is unreachable + let c = 3; + println!("{}", c); +} + +fn after_unreachable() { + unsafe { std::hint::unreachable_unchecked() } + + // This should not warn - the code is unreachable + let d = 4; + println!("{}", d); +} + +fn reachable_unused() { + // This SHOULD warn - the code is reachable + let e = 5; //~ WARN unused variable: `e` +} + +fn main() {} diff --git a/tests/ui/lint/unused/unused-var-in-unreachable-code.stderr b/tests/ui/lint/unused/unused-var-in-unreachable-code.stderr new file mode 100644 index 0000000000000..dfc2f9c8fc56a --- /dev/null +++ b/tests/ui/lint/unused/unused-var-in-unreachable-code.stderr @@ -0,0 +1,14 @@ +warning: unused variable: `e` + --> $DIR/unused-var-in-unreachable-code.rs:43:9 + | +LL | let e = 5; + | ^ help: if this is intentional, prefix it with an underscore: `_e` + | +note: the lint level is defined here + --> $DIR/unused-var-in-unreachable-code.rs:5:9 + | +LL | #![warn(unused_variables)] + | ^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/ui/return/early-return-with-unreachable-code-24353.rs b/tests/ui/return/early-return-with-unreachable-code-24353.rs index 41d77fea49ac9..13add4652d994 100644 --- a/tests/ui/return/early-return-with-unreachable-code-24353.rs +++ b/tests/ui/return/early-return-with-unreachable-code-24353.rs @@ -4,7 +4,6 @@ fn main() { return (); let x = (); - //~^ WARN unused variable: `x` x } diff --git a/tests/ui/return/early-return-with-unreachable-code-24353.stderr b/tests/ui/return/early-return-with-unreachable-code-24353.stderr deleted file mode 100644 index 92526faef33bb..0000000000000 --- a/tests/ui/return/early-return-with-unreachable-code-24353.stderr +++ /dev/null @@ -1,10 +0,0 @@ -warning: unused variable: `x` - --> $DIR/early-return-with-unreachable-code-24353.rs:6:9 - | -LL | let x = (); - | ^ help: if this is intentional, prefix it with an underscore: `_x` - | - = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default - -warning: 1 warning emitted -