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
This commit is contained in:
commit
445d41909e
4 changed files with 85 additions and 34 deletions
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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>>),
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue