about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorAlejandra González <blyxyas@gmail.com>2025-07-30 16:42:12 +0000
committerGitHub <noreply@github.com>2025-07-30 16:42:12 +0000
commit445d41909e117ea4c23401e70bbbe5ca9a25e455 (patch)
tree5bba4b1bcabd1254158db589c18b2d7d9b2cee2a /clippy_lints/src
parent18e8ac3778a2859d412c34b0ff7a1f15c07c9103 (diff)
parent20670f78fdd63fbf1597457fcb35a562a428e0e9 (diff)
Fix `iter_on_single_items` FP on function pointers and let stmts (#15013)
Closes rust-lang/rust-clippy#14981

changelog: [`iter_on_single_items`] fix FP on function pointers and let
stmts
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/methods/iter_on_single_or_empty_collections.rs69
1 files changed, 36 insertions, 33 deletions
diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
index c0366765234..83e565562af 100644
--- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
+++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
@@ -2,14 +2,15 @@ use std::iter::once;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
+use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig};
 use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core, sym};
 
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::def_id::DefId;
 use rustc_hir::hir_id::HirId;
 use rustc_hir::{Expr, ExprKind, Node};
 use rustc_lint::LateContext;
+use rustc_middle::ty::Binder;
 use rustc_span::Symbol;
 
 use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
@@ -32,24 +33,34 @@ impl IterType {
 
 fn is_arg_ty_unified_in_fn<'tcx>(
     cx: &LateContext<'tcx>,
-    fn_id: DefId,
+    fn_sig: ExprFnSig<'tcx>,
     arg_id: HirId,
     args: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
+    is_method: bool,
 ) -> bool {
-    let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity();
     let arg_id_in_args = args.into_iter().position(|e| e.hir_id == arg_id).unwrap();
-    let arg_ty_in_args = fn_sig.input(arg_id_in_args).skip_binder();
+    let Some(arg_ty_in_args) = fn_sig.input(arg_id_in_args).map(Binder::skip_binder) else {
+        return false;
+    };
 
-    cx.tcx.predicates_of(fn_id).predicates.iter().any(|(clause, _)| {
-        clause
-            .as_projection_clause()
-            .and_then(|p| p.map_bound(|p| p.term.as_type()).transpose())
-            .is_some_and(|ty| ty.skip_binder() == arg_ty_in_args)
-    }) || fn_sig
-        .inputs()
-        .iter()
-        .enumerate()
-        .any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args)))
+    fn_sig
+        .predicates_id()
+        .map(|def_id| cx.tcx.predicates_of(def_id))
+        .is_some_and(|generics| {
+            generics.predicates.iter().any(|(clause, _)| {
+                clause
+                    .as_projection_clause()
+                    .and_then(|p| p.map_bound(|p| p.term.as_type()).transpose())
+                    .is_some_and(|ty| ty.skip_binder() == arg_ty_in_args)
+            })
+        })
+        || (!is_method
+            && fn_sig.input(arg_id_in_args).is_some_and(|binder| {
+                binder
+                    .skip_binder()
+                    .walk()
+                    .any(|arg| arg.as_type() == Some(arg_ty_in_args))
+            }))
 }
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, recv: &'tcx Expr<'tcx>) {
@@ -70,25 +81,16 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method
     let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
         Some((Node::Expr(parent), child_id)) => match parent.kind {
             ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
-            ExprKind::Call(
-                Expr {
-                    kind: ExprKind::Path(path),
-                    hir_id,
-                    ..
-                },
-                args,
-            ) => cx
+            ExprKind::Call(recv, args) => {
+                expr_sig(cx, recv).is_some_and(|fn_sig| is_arg_ty_unified_in_fn(cx, fn_sig, child_id, args, false))
+            },
+            ExprKind::MethodCall(_name, recv, args, _span) => cx
                 .typeck_results()
-                .qpath_res(path, *hir_id)
-                .opt_def_id()
-                .filter(|fn_id| cx.tcx.def_kind(fn_id).is_fn_like())
-                .is_some_and(|fn_id| is_arg_ty_unified_in_fn(cx, fn_id, child_id, args)),
-            ExprKind::MethodCall(_name, recv, args, _span) => is_arg_ty_unified_in_fn(
-                cx,
-                cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(),
-                child_id,
-                once(recv).chain(args.iter()),
-            ),
+                .type_dependent_def_id(parent.hir_id)
+                .and_then(|def_id| ty_sig(cx, cx.tcx.type_of(def_id).instantiate_identity()))
+                .is_some_and(|fn_sig| {
+                    is_arg_ty_unified_in_fn(cx, fn_sig, child_id, once(recv).chain(args.iter()), true)
+                }),
             ExprKind::If(_, _, _)
             | ExprKind::Match(_, _, _)
             | ExprKind::Closure(_)
@@ -96,7 +98,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method
             | ExprKind::Break(_, _) => true,
             _ => false,
         },
-        Some((Node::Stmt(_) | Node::LetStmt(_), _)) => false,
+        Some((Node::LetStmt(let_stmt), _)) => let_stmt.ty.is_some(),
+        Some((Node::Stmt(_), _)) => false,
         _ => true,
     };