about summary refs log tree commit diff
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
parent18e8ac3778a2859d412c34b0ff7a1f15c07c9103 (diff)
parent20670f78fdd63fbf1597457fcb35a562a428e0e9 (diff)
downloadrust-445d41909e117ea4c23401e70bbbe5ca9a25e455.tar.gz
rust-445d41909e117ea4c23401e70bbbe5ca9a25e455.zip
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
-rw-r--r--clippy_lints/src/methods/iter_on_single_or_empty_collections.rs69
-rw-r--r--clippy_utils/src/ty/mod.rs2
-rw-r--r--tests/ui/iter_on_single_items.fixed24
-rw-r--r--tests/ui/iter_on_single_items.rs24
4 files changed, 85 insertions, 34 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,
     };
 
diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs
index d70232ef3aa..f480d2afd30 100644
--- a/clippy_utils/src/ty/mod.rs
+++ b/clippy_utils/src/ty/mod.rs
@@ -582,7 +582,7 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(t
 }
 
 /// A signature for a function like type.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
 pub enum ExprFnSig<'tcx> {
     Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
     Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
diff --git a/tests/ui/iter_on_single_items.fixed b/tests/ui/iter_on_single_items.fixed
index b43fad6449c..044037aac2e 100644
--- a/tests/ui/iter_on_single_items.fixed
+++ b/tests/ui/iter_on_single_items.fixed
@@ -66,3 +66,27 @@ fn main() {
     custom_option::custom_option();
     in_macros!();
 }
+
+mod issue14981 {
+    use std::option::IntoIter;
+    fn takes_into_iter(_: impl IntoIterator<Item = i32>) {}
+
+    fn let_stmt() {
+        macro_rules! x {
+            ($e:expr) => {
+                let _: IntoIter<i32> = $e;
+            };
+        }
+        x!(Some(5).into_iter());
+    }
+
+    fn fn_ptr() {
+        fn some_func(_: IntoIter<i32>) -> IntoIter<i32> {
+            todo!()
+        }
+        some_func(Some(5).into_iter());
+
+        const C: fn(IntoIter<i32>) -> IntoIter<i32> = <IntoIter<i32> as IntoIterator>::into_iter;
+        C(Some(5).into_iter());
+    }
+}
diff --git a/tests/ui/iter_on_single_items.rs b/tests/ui/iter_on_single_items.rs
index 625c96d3ef1..c925d0e480f 100644
--- a/tests/ui/iter_on_single_items.rs
+++ b/tests/ui/iter_on_single_items.rs
@@ -66,3 +66,27 @@ fn main() {
     custom_option::custom_option();
     in_macros!();
 }
+
+mod issue14981 {
+    use std::option::IntoIter;
+    fn takes_into_iter(_: impl IntoIterator<Item = i32>) {}
+
+    fn let_stmt() {
+        macro_rules! x {
+            ($e:expr) => {
+                let _: IntoIter<i32> = $e;
+            };
+        }
+        x!(Some(5).into_iter());
+    }
+
+    fn fn_ptr() {
+        fn some_func(_: IntoIter<i32>) -> IntoIter<i32> {
+            todo!()
+        }
+        some_func(Some(5).into_iter());
+
+        const C: fn(IntoIter<i32>) -> IntoIter<i32> = <IntoIter<i32> as IntoIterator>::into_iter;
+        C(Some(5).into_iter());
+    }
+}