diff options
| author | Alejandra González <blyxyas@gmail.com> | 2025-07-30 16:42:12 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-30 16:42:12 +0000 |
| commit | 445d41909e117ea4c23401e70bbbe5ca9a25e455 (patch) | |
| tree | 5bba4b1bcabd1254158db589c18b2d7d9b2cee2a | |
| parent | 18e8ac3778a2859d412c34b0ff7a1f15c07c9103 (diff) | |
| parent | 20670f78fdd63fbf1597457fcb35a562a428e0e9 (diff) | |
| download | rust-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.rs | 69 | ||||
| -rw-r--r-- | clippy_utils/src/ty/mod.rs | 2 | ||||
| -rw-r--r-- | tests/ui/iter_on_single_items.fixed | 24 | ||||
| -rw-r--r-- | tests/ui/iter_on_single_items.rs | 24 |
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()); + } +} |
