From 22b3f598821a7262e06f57542cdf146108709df2 Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Fri, 23 Jan 2026 22:20:09 +0000 Subject: [PATCH] Fix suppression of `unused_assignment` in binding of `unused_variable` Unused assignments to an unused variable should trigger only the `unused_variables` lint and not also the `unused_assignments` lint. This was previously implemented by checking whether the span of the assignee was within the span of the binding pattern, however that failed to capture situations was imported from elsewhere (eg from the input tokenstream of a proc-macro that generates the binding pattern). By comparing the span of the assignee to those of the variable introductions instead, a reported stable-to-stable regression is resolved. This fix also impacted some other preexisting tests, which had (undesirably) been triggering both the `unused_variables` and `unused_assignments` lints on the same initializing assignment; those tests have therefore now been updated to expect only the former lint. --- compiler/rustc_mir_transform/src/liveness.rs | 2 +- ...orthand-field-patterns-in-pattern-macro.rs | 4 ++-- ...and-field-patterns-in-pattern-macro.stderr | 12 ++++++---- .../auxiliary/unused_assignment_proc_macro.rs | 23 +++++++++++++++++++ tests/ui/lint/unused/unused_assignment.rs | 21 +++++++++++++++++ tests/ui/lint/unused/unused_assignment.stderr | 15 ++++++++++++ .../pattern/bindings-after-at/bind-by-copy.rs | 1 - .../bindings-after-at/bind-by-copy.stderr | 12 ++-------- 8 files changed, 72 insertions(+), 18 deletions(-) create mode 100644 tests/ui/lint/unused/auxiliary/unused_assignment_proc_macro.rs create mode 100644 tests/ui/lint/unused/unused_assignment.rs create mode 100644 tests/ui/lint/unused/unused_assignment.stderr diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 1d1ba455a81e..0d3d162109da 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -986,7 +986,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { // warn twice, for the unused local and for the unused assignment. Therefore, we remove // from the list of assignments the ones that happen at the definition site. statements.retain(|source_info, _| { - source_info.span.find_ancestor_inside(binding.pat_span).is_none() + !binding.introductions.iter().any(|intro| intro.span == source_info.span) }); // Extra assignments that we recognize thanks to the initialization span. We need to diff --git a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs index 5b5843a8ddbb..570b559eb61a 100644 --- a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs +++ b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs @@ -1,5 +1,5 @@ //@ run-pass -#![allow(unused_variables)] +#![warn(unused)] #![deny(non_shorthand_field_patterns)] pub struct Value { pub value: A } @@ -13,5 +13,5 @@ macro_rules! pat { fn main() { let pat!(value) = Value { value: () }; - //~^ WARN value assigned to `value` is never read + //~^ WARN unused variable: `value` } diff --git a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr index ba7d3515b0d8..3a68ec212b4a 100644 --- a/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr +++ b/tests/ui/lint/issue-49588-non-shorthand-field-patterns-in-pattern-macro.stderr @@ -1,11 +1,15 @@ -warning: value assigned to `value` is never read +warning: unused variable: `value` --> $DIR/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs:15:14 | LL | let pat!(value) = Value { value: () }; - | ^^^^^ + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_value` | - = help: maybe it is overwritten before being read? - = note: `#[warn(unused_assignments)]` (part of `#[warn(unused)]`) on by default +note: the lint level is defined here + --> $DIR/issue-49588-non-shorthand-field-patterns-in-pattern-macro.rs:2:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` warning: 1 warning emitted diff --git a/tests/ui/lint/unused/auxiliary/unused_assignment_proc_macro.rs b/tests/ui/lint/unused/auxiliary/unused_assignment_proc_macro.rs new file mode 100644 index 000000000000..41cfefbaff54 --- /dev/null +++ b/tests/ui/lint/unused/auxiliary/unused_assignment_proc_macro.rs @@ -0,0 +1,23 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_derive(Drop)] +pub fn generate(ts: TokenStream) -> TokenStream { + let mut ts = ts.into_iter(); + let _pub = ts.next(); + let _struct = ts.next(); + let name = ts.next().unwrap(); + let TokenTree::Group(fields) = ts.next().unwrap() else { panic!() }; + let mut fields = fields.stream().into_iter(); + let field = fields.next().unwrap(); + + quote! { + impl Drop for $name { + fn drop(&mut self) { + let Self { $field } = self; + } + } + } +} diff --git a/tests/ui/lint/unused/unused_assignment.rs b/tests/ui/lint/unused/unused_assignment.rs new file mode 100644 index 000000000000..f7b8ec94bc3b --- /dev/null +++ b/tests/ui/lint/unused/unused_assignment.rs @@ -0,0 +1,21 @@ +// Unused assignments to an unused variable should trigger only the `unused_variables` lint and not +// also the `unused_assignments` lint. This test covers the situation where the span of the unused +// variable identifier comes from a different scope to the binding pattern - here, from a proc +// macro's input tokenstream (whereas the binding pattern is generated within the proc macro +// itself). +// +// Regression test for https://github.com/rust-lang/rust/issues/151514 +// +//@ check-pass +//@ proc-macro: unused_assignment_proc_macro.rs +#![warn(unused)] + +extern crate unused_assignment_proc_macro; +use unused_assignment_proc_macro::Drop; + +#[derive(Drop)] +pub struct S { + a: (), //~ WARN unused variable: `a` +} + +fn main() {} diff --git a/tests/ui/lint/unused/unused_assignment.stderr b/tests/ui/lint/unused/unused_assignment.stderr new file mode 100644 index 000000000000..1f0619ecf142 --- /dev/null +++ b/tests/ui/lint/unused/unused_assignment.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `a` + --> $DIR/unused_assignment.rs:18:5 + | +LL | a: (), + | ^ help: try ignoring the field: `a: _` + | +note: the lint level is defined here + --> $DIR/unused_assignment.rs:11:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy.rs b/tests/ui/pattern/bindings-after-at/bind-by-copy.rs index d766411e4f98..3b6f2a9b08f7 100644 --- a/tests/ui/pattern/bindings-after-at/bind-by-copy.rs +++ b/tests/ui/pattern/bindings-after-at/bind-by-copy.rs @@ -53,7 +53,6 @@ pub fn main() { } match (E::E { a: 10, e: C { c: 20 } }) { mut x @ E::E{ a, e: C { mut c } } => { - //~^ WARN value assigned to `a` is never read x = E::NotE; //~^ WARN value assigned to `x` is never read c += 30; diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr b/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr index d775b69ef0a5..d0128950ddda 100644 --- a/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr +++ b/tests/ui/pattern/bindings-after-at/bind-by-copy.stderr @@ -20,20 +20,12 @@ LL | y.d.c = 30; = help: maybe it is overwritten before being read? warning: value assigned to `x` is never read - --> $DIR/bind-by-copy.rs:57:13 + --> $DIR/bind-by-copy.rs:56:13 | LL | x = E::NotE; | ^^^^^^^^^^^ | = help: maybe it is overwritten before being read? -warning: value assigned to `a` is never read - --> $DIR/bind-by-copy.rs:55:23 - | -LL | mut x @ E::E{ a, e: C { mut c } } => { - | ^ - | - = help: maybe it is overwritten before being read? - -warning: 4 warnings emitted +warning: 3 warnings emitted