From 74a77047da404a0b69db4b58957127e415c4d20d Mon Sep 17 00:00:00 2001
From: Catherine <114838443+Centri3@users.noreply.github.com>
Date: Mon, 17 Jul 2023 05:33:41 -0500
Subject: [PATCH] Rewrite [`tuple_array_conversions`]
---
clippy_lints/src/tuple_array_conversions.rs | 314 +++++++++-----------
tests/ui/tuple_array_conversions.rs | 30 ++
tests/ui/tuple_array_conversions.stderr | 36 +--
3 files changed, 195 insertions(+), 185 deletions(-)
diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs
index 2e00ed042a8b..791cbe2fead7 100644
--- a/clippy_lints/src/tuple_array_conversions.rs
+++ b/clippy_lints/src/tuple_array_conversions.rs
@@ -1,21 +1,29 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::visitors::for_each_local_use_after_expr;
use clippy_utils::{is_from_proc_macro, path_to_local};
+use itertools::Itertools;
use rustc_ast::LitKind;
-use rustc_hir::{Expr, ExprKind, HirId, Node, Pat};
+use rustc_hir::{Expr, ExprKind, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
+use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use std::iter::once;
+use std::ops::ControlFlow;
declare_clippy_lint! {
/// ### What it does
/// Checks for tuple<=>array conversions that are not done with `.into()`.
///
/// ### Why is this bad?
- /// It may be unnecessary complexity. `.into()` works for converting tuples
- /// <=> arrays of up to 12 elements and may convey intent more clearly.
+ /// It may be unnecessary complexity. `.into()` works for converting tuples<=> arrays of up to
+ /// 12 elements and conveys the intent more clearly, while also leaving less room for hard to
+ /// spot bugs!
+ ///
+ /// ### Known issues
+ /// The suggested code may hide potential asymmetry in some cases. See
+ /// [#11085](https://github.com/rust-lang/rust-clippy/issues/11085) for more info.
///
/// ### Example
/// ```rust,ignore
@@ -41,130 +49,152 @@ pub struct TupleArrayConversions {
impl LateLintPass<'_> for TupleArrayConversions {
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::TUPLE_ARRAY_CONVERSIONS) {
- match expr.kind {
- ExprKind::Array(elements) if (1..=12).contains(&elements.len()) => check_array(cx, expr, elements),
- ExprKind::Tup(elements) if (1..=12).contains(&elements.len()) => check_tuple(cx, expr, elements),
- _ => {},
- }
+ if in_external_macro(cx.sess(), expr.span) || !self.msrv.meets(msrvs::TUPLE_ARRAY_CONVERSIONS) {
+ return;
+ }
+
+ match expr.kind {
+ ExprKind::Array(elements) if (1..=12).contains(&elements.len()) => check_array(cx, expr, elements),
+ ExprKind::Tup(elements) if (1..=12).contains(&elements.len()) => check_tuple(cx, expr, elements),
+ _ => {},
}
}
extract_msrv_attr!(LateContext);
}
-#[expect(
- clippy::blocks_in_if_conditions,
- reason = "not a FP, but this is much easier to understand"
-)]
fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) {
- if should_lint(
- cx,
- elements,
- // This is cursed.
- Some,
- |(first_id, local)| {
- if let Node::Pat(pat) = local
- && let parent = parent_pat(cx, pat)
- && parent.hir_id == first_id
- {
- return matches!(
- cx.typeck_results().pat_ty(parent).peel_refs().kind(),
- ty::Tuple(len) if len.len() == elements.len()
- );
- }
-
- false
- },
- ) || should_lint(
- cx,
- elements,
- |(i, expr)| {
- if let ExprKind::Field(path, field) = expr.kind && field.as_str() == i.to_string() {
- return Some((i, path));
- };
-
- None
- },
- |(first_id, local)| {
- if let Node::Pat(pat) = local
- && let parent = parent_pat(cx, pat)
- && parent.hir_id == first_id
- {
- return matches!(
- cx.typeck_results().pat_ty(parent).peel_refs().kind(),
- ty::Tuple(len) if len.len() == elements.len()
- );
- }
+ let (ty::Array(ty, _) | ty::Slice(ty)) = cx.typeck_results().expr_ty(expr).kind() else {
+ unreachable!("`expr` must be an array or slice due to `ExprKind::Array`");
+ };
+
+ if let [first, ..] = elements
+ && let Some(locals) = (match first.kind {
+ ExprKind::Field(_, _) => elements
+ .iter()
+ .enumerate()
+ .map(|(i, f)| -> Option<&'tcx Expr<'tcx>> {
+ let ExprKind::Field(lhs, ident) = f.kind else {
+ return None;
+ };
+ (ident.name.as_str() == i.to_string()).then_some(lhs)
+ })
+ .collect::