Fix tuple_array_conversions FP when binded vars are used before conversion (#16197)

Closes rust-lang/rust-clippy#16192

changelog: [`tuple_array_conversions`] fix FP when binded vars are used
before conversion
This commit is contained in:
Jason Newcomb 2025-12-09 17:05:01 +00:00 committed by GitHub
commit 52fea21d5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 13 deletions

View file

@ -1,9 +1,9 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_from_proc_macro;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeResPath;
use clippy_utils::visitors::for_each_local_use_after_expr;
use clippy_utils::visitors::local_used_once;
use clippy_utils::{get_enclosing_block, is_from_proc_macro};
use itertools::Itertools;
use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind, Node, PatKind};
@ -11,7 +11,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
use std::iter::once;
use std::ops::ControlFlow;
declare_clippy_lint! {
/// ### What it does
@ -86,7 +85,7 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &
ExprKind::Path(_) => Some(elements.iter().collect()),
_ => None,
})
&& all_bindings_are_for_conv(cx, &[ty], expr, elements, &locals, ToType::Array)
&& all_bindings_are_for_conv(cx, &[ty], elements, &locals, ToType::Array)
&& !is_from_proc_macro(cx, expr)
{
span_lint_and_help(
@ -123,7 +122,7 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &
ExprKind::Path(_) => Some(elements.iter().collect()),
_ => None,
})
&& all_bindings_are_for_conv(cx, tys, expr, elements, &locals, ToType::Tuple)
&& all_bindings_are_for_conv(cx, tys, elements, &locals, ToType::Tuple)
&& !is_from_proc_macro(cx, expr)
{
span_lint_and_help(
@ -148,7 +147,6 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &
fn all_bindings_are_for_conv<'tcx>(
cx: &LateContext<'tcx>,
final_tys: &[Ty<'tcx>],
expr: &Expr<'_>,
elements: &[Expr<'_>],
locals: &[&Expr<'_>],
kind: ToType,
@ -166,13 +164,30 @@ fn all_bindings_are_for_conv<'tcx>(
_ => None,
})
.all_equal()
// Fix #11124, very convenient utils function! ❤️
&& locals
.iter()
.all(|&l| for_each_local_use_after_expr(cx, l, expr.hir_id, |_| ControlFlow::Break::<()>(())).is_continue())
&& locals.iter().zip(local_parents.iter()).all(|(&l, &parent)| {
if let Node::LetStmt(_) = parent {
return true;
}
let Some(b) = get_enclosing_block(cx, l) else {
return true;
};
local_used_once(cx, b, l).is_some()
})
&& local_parents.first().is_some_and(|node| {
let Some(ty) = match node {
Node::Pat(pat) => Some(pat.hir_id),
Node::Pat(pat)
if let PatKind::Tuple(pats, _) | PatKind::Slice(pats, None, []) = &pat.kind
&& pats.iter().zip(locals.iter()).all(|(p, l)| {
if let PatKind::Binding(_, id, _, _) = p.kind {
id == *l
} else {
true
}
}) =>
{
Some(pat.hir_id)
},
Node::LetStmt(l) => Some(l.hir_id),
_ => None,
}
@ -186,7 +201,9 @@ fn all_bindings_are_for_conv<'tcx>(
tys.len() == elements.len() && tys.iter().chain(final_tys.iter().copied()).all_equal()
},
(ToType::Tuple, ty::Array(ty, len)) => {
let Some(len) = len.try_to_target_usize(cx.tcx) else { return false };
let Some(len) = len.try_to_target_usize(cx.tcx) else {
return false;
};
len as usize == elements.len() && final_tys.iter().chain(once(ty)).all_equal()
},
_ => false,

View file

@ -116,3 +116,26 @@ fn msrv_juust_right() {
let x = &[1, 2];
let x = (x[0], x[1]);
}
fn issue16192() {
fn do_something(tuple: (u32, u32)) {}
fn produce_array() -> [u32; 2] {
[1, 2]
}
let [a, b] = produce_array();
for tuple in [(a, b), (b, a)] {
do_something(tuple);
}
let [a, b] = produce_array();
let x = b;
do_something((a, b));
let [a, b] = produce_array();
do_something((b, a));
let [a, b] = produce_array();
do_something((a, b));
//~^ tuple_array_conversions
}

View file

@ -80,5 +80,13 @@ LL | let x = [x.0, x.1];
|
= help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
error: aborting due to 10 previous errors
error: it looks like you're trying to convert an array to a tuple
--> tests/ui/tuple_array_conversions.rs:139:18
|
LL | do_something((a, b));
| ^^^^^^
|
= help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed
error: aborting due to 11 previous errors