From 9e9c9170a50ba34a3cb66540a2079884b92ed480 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 8 Jun 2025 00:49:00 +0800 Subject: [PATCH 1/2] fix: `iter_on_single_items` FP on function pointers --- .../iter_on_single_or_empty_collections.rs | 65 ++++++++++--------- clippy_utils/src/ty/mod.rs | 2 +- tests/ui/iter_on_single_items.fixed | 11 ++++ tests/ui/iter_on_single_items.rs | 11 ++++ 4 files changed, 56 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 c0366765234f..bda74ab173b6 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,11 +2,11 @@ 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; @@ -32,24 +32,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>, + 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| 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 +80,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(_) diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 1f0a0f2b02ac..c9100d97bcfc 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -581,7 +581,7 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator { Sig(Binder<'tcx, FnSig<'tcx>>, Option), 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 b43fad6449c1..bdab6121b1f6 100644 --- a/tests/ui/iter_on_single_items.fixed +++ b/tests/ui/iter_on_single_items.fixed @@ -66,3 +66,14 @@ fn main() { custom_option::custom_option(); in_macros!(); } + +fn issue14981() { + use std::option::IntoIter; + fn some_func(_: IntoIter) -> IntoIter { + todo!() + } + some_func(Some(5).into_iter()); + + const C: fn(IntoIter) -> IntoIter = 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 625c96d3ef1f..40ed6d3ec1a6 100644 --- a/tests/ui/iter_on_single_items.rs +++ b/tests/ui/iter_on_single_items.rs @@ -66,3 +66,14 @@ fn main() { custom_option::custom_option(); in_macros!(); } + +fn issue14981() { + use std::option::IntoIter; + fn some_func(_: IntoIter) -> IntoIter { + todo!() + } + some_func(Some(5).into_iter()); + + const C: fn(IntoIter) -> IntoIter = as IntoIterator>::into_iter; + C(Some(5).into_iter()); +} From 20670f78fdd63fbf1597457fcb35a562a428e0e9 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 8 Jun 2025 01:13:18 +0800 Subject: [PATCH 2/2] fix: `iter_on_single_item` FP on let stmt with type annotation --- .../iter_on_single_or_empty_collections.rs | 6 +++-- tests/ui/iter_on_single_items.fixed | 25 ++++++++++++++----- tests/ui/iter_on_single_items.rs | 25 ++++++++++++++----- 3 files changed, 42 insertions(+), 14 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 bda74ab173b6..83e565562af0 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 @@ -10,6 +10,7 @@ use rustc_hir::LangItem::{OptionNone, OptionSome}; 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}; @@ -38,7 +39,7 @@ fn is_arg_ty_unified_in_fn<'tcx>( is_method: bool, ) -> bool { let arg_id_in_args = args.into_iter().position(|e| e.hir_id == arg_id).unwrap(); - let Some(arg_ty_in_args) = fn_sig.input(arg_id_in_args).map(|binder| binder.skip_binder()) else { + let Some(arg_ty_in_args) = fn_sig.input(arg_id_in_args).map(Binder::skip_binder) else { return false; }; @@ -97,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/tests/ui/iter_on_single_items.fixed b/tests/ui/iter_on_single_items.fixed index bdab6121b1f6..044037aac2e3 100644 --- a/tests/ui/iter_on_single_items.fixed +++ b/tests/ui/iter_on_single_items.fixed @@ -67,13 +67,26 @@ fn main() { in_macros!(); } -fn issue14981() { +mod issue14981 { use std::option::IntoIter; - fn some_func(_: IntoIter) -> IntoIter { - todo!() + fn takes_into_iter(_: impl IntoIterator) {} + + fn let_stmt() { + macro_rules! x { + ($e:expr) => { + let _: IntoIter = $e; + }; + } + x!(Some(5).into_iter()); } - some_func(Some(5).into_iter()); - const C: fn(IntoIter) -> IntoIter = as IntoIterator>::into_iter; - C(Some(5).into_iter()); + fn fn_ptr() { + fn some_func(_: IntoIter) -> IntoIter { + todo!() + } + some_func(Some(5).into_iter()); + + const C: fn(IntoIter) -> IntoIter = 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 40ed6d3ec1a6..c925d0e480fa 100644 --- a/tests/ui/iter_on_single_items.rs +++ b/tests/ui/iter_on_single_items.rs @@ -67,13 +67,26 @@ fn main() { in_macros!(); } -fn issue14981() { +mod issue14981 { use std::option::IntoIter; - fn some_func(_: IntoIter) -> IntoIter { - todo!() + fn takes_into_iter(_: impl IntoIterator) {} + + fn let_stmt() { + macro_rules! x { + ($e:expr) => { + let _: IntoIter = $e; + }; + } + x!(Some(5).into_iter()); } - some_func(Some(5).into_iter()); - const C: fn(IntoIter) -> IntoIter = as IntoIterator>::into_iter; - C(Some(5).into_iter()); + fn fn_ptr() { + fn some_func(_: IntoIter) -> IntoIter { + todo!() + } + some_func(Some(5).into_iter()); + + const C: fn(IntoIter) -> IntoIter = as IntoIterator>::into_iter; + C(Some(5).into_iter()); + } }