Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions compiler/rustc_mir_transform/src/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,49 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
false
}

/// Check for source-level uses that may have been removed from reachable MIR.
fn is_local_used_in_source(&self, name: Symbol, def_span: Span) -> bool {
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{self, Visitor};

let Some(body_def_id) = self.body.source.def_id().as_local() else { return false };
let Some(hir_body) = self.tcx.hir_maybe_body_owned_by(body_def_id) else { return false };
let typeck_results = self.tcx.typeck(body_def_id);

struct LocalUseVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>,
name: Symbol,
def_span: Span,
found: bool,
}

impl<'a, 'tcx> Visitor<'tcx> for LocalUseVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
if self.found {
return;
}

if let hir::ExprKind::Path(qpath) = &expr.kind
&& let Res::Local(hir_id) = self.typeck_results.qpath_res(qpath, expr.hir_id)
&& self.tcx.hir_name(hir_id) == self.name
&& self.tcx.hir_span(hir_id) == self.def_span
{
self.found = true;
return;
}

intravisit::walk_expr(self, expr);
}
}

let mut visitor =
LocalUseVisitor { tcx: self.tcx, typeck_results, name, def_span, found: false };
visitor.visit_body(hir_body);
visitor.found
}

/// Report fully unused locals, and forget the corresponding assignments.
fn report_fully_unused(&mut self) {
let tcx = self.tcx;
Expand Down Expand Up @@ -995,8 +1038,20 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> {
}
};

let is_used_after_uninitialized = self.body.local_kind(local) == LocalKind::Temp
&& matches!(binding.opt_match_place, Some((None, _)))
&& self.is_local_used_in_source(name, def_span);

let statements = &mut self.assignments[index];
if statements.is_empty() {
if is_used_after_uninitialized {
// A local from `let PAT = ...` normally has an assignment recorded for the
// value it initializes. If no assignment was recorded in reachable MIR, the
// initializer did not complete. If the local still has a source-level use,
// that use was made unreachable by the diverging initializer.
continue;
}

if !self.is_local_in_reachable_code(local) {
continue;
}
Expand Down
39 changes: 39 additions & 0 deletions tests/ui/lint/unused/unused-never-binding-issue-158783.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//@ check-pass

#![warn(unused_variables)]

// A binding initialized by a diverging expression should not be reported as unused.

fn diverge() -> ! {
loop {}
}

fn test1() {
let res: Result<u32, u32> = diverge();
eprintln!("{:?}", res);
}

fn foo() -> ! {
todo!()
}

fn test2() {
let res: Result<u32, u32> = foo();
match res {
Ok(v) => todo!(),
Err(err) => todo!(),
}
}

fn test3() -> i32 {
let x = {
//~^ WARN unused variable: `x`
return 5;
};
}

fn main() {
test1();
test2();
test3();
}
14 changes: 14 additions & 0 deletions tests/ui/lint/unused/unused-never-binding-issue-158783.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
warning: unused variable: `x`
--> $DIR/unused-never-binding-issue-158783.rs:29:9
|
LL | let x = {
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
note: the lint level is defined here
--> $DIR/unused-never-binding-issue-158783.rs:3:9
|
LL | #![warn(unused_variables)]
| ^^^^^^^^^^^^^^^^

warning: 1 warning emitted

2 changes: 1 addition & 1 deletion tests/ui/reachable/never-assign-dead-code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#![warn(unused)]

fn main() {
let x: ! = panic!("aah"); //~ WARN unused
let x: ! = panic!("aah");
drop(x); //~ WARN unreachable
//~^ WARN unreachable
}
10 changes: 1 addition & 9 deletions tests/ui/reachable/never-assign-dead-code.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,5 @@ LL | drop(x);
| |
| unreachable call

warning: unused variable: `x`
--> $DIR/never-assign-dead-code.rs:10:9
|
LL | let x: ! = panic!("aah");
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`

warning: 3 warnings emitted
warning: 2 warnings emitted

Loading