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.
This commit is contained in:
Alan Egerton 2026-01-23 22:20:09 +00:00
parent 86a49fd71f
commit 22b3f59882
No known key found for this signature in database
GPG key ID: 3D7EA7527916B438
8 changed files with 72 additions and 18 deletions

View file

@ -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

View file

@ -1,5 +1,5 @@
//@ run-pass
#![allow(unused_variables)]
#![warn(unused)]
#![deny(non_shorthand_field_patterns)]
pub struct Value<A> { 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`
}

View file

@ -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

View file

@ -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;
}
}
}
}

View file

@ -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() {}

View file

@ -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

View file

@ -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;

View file

@ -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