1 use super::utils
::clone_or_copy_needed
;
2 use clippy_utils
::diagnostics
::span_lint_and_then
;
3 use clippy_utils
::higher
::ForLoop
;
4 use clippy_utils
::source
::snippet_opt
;
5 use clippy_utils
::ty
::{get_associated_type, get_iterator_item_ty, implements_trait}
;
6 use clippy_utils
::{fn_def_id, get_parent_expr}
;
7 use rustc_errors
::Applicability
;
8 use rustc_hir
::{def_id::DefId, Expr, ExprKind, LangItem}
;
9 use rustc_lint
::LateContext
;
10 use rustc_span
::{sym, Symbol}
;
12 use super::UNNECESSARY_TO_OWNED
;
14 pub fn check(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>, method_name
: Symbol
, receiver
: &Expr
<'_
>) -> bool
{
16 if let Some(parent
) = get_parent_expr(cx
, expr
);
17 if let Some(callee_def_id
) = fn_def_id(cx
, parent
);
18 if is_into_iter(cx
, callee_def_id
);
20 check_for_loop_iter(cx
, parent
, method_name
, receiver
, false)
27 /// Checks whether `expr` is an iterator in a `for` loop and, if so, determines whether the
28 /// iterated-over items could be iterated over by reference. The reason why `check` above does not
29 /// include this code directly is so that it can be called from
30 /// `unnecessary_into_owned::check_into_iter_call_arg`.
31 pub fn check_for_loop_iter(
36 cloned_before_iter
: bool
,
39 if let Some(grandparent
) = get_parent_expr(cx
, expr
).and_then(|parent
| get_parent_expr(cx
, parent
));
40 if let Some(ForLoop { pat, body, .. }
) = ForLoop
::hir(grandparent
);
41 let (clone_or_copy_needed
, addr_of_exprs
) = clone_or_copy_needed(cx
, pat
, body
);
42 if !clone_or_copy_needed
;
43 if let Some(receiver_snippet
) = snippet_opt(cx
, receiver
.span
);
45 let snippet
= if_chain
! {
46 if let ExprKind
::MethodCall(maybe_iter_method_name
, collection
, [], _
) = receiver
.kind
;
47 if maybe_iter_method_name
.ident
.name
== sym
::iter
;
49 if let Some(iterator_trait_id
) = cx
.tcx
.get_diagnostic_item(sym
::Iterator
);
50 let receiver_ty
= cx
.typeck_results().expr_ty(receiver
);
51 if implements_trait(cx
, receiver_ty
, iterator_trait_id
, &[]);
52 if let Some(iter_item_ty
) = get_iterator_item_ty(cx
, receiver_ty
);
54 if let Some(into_iterator_trait_id
) = cx
.tcx
.get_diagnostic_item(sym
::IntoIterator
);
55 let collection_ty
= cx
.typeck_results().expr_ty(collection
);
56 if implements_trait(cx
, collection_ty
, into_iterator_trait_id
, &[]);
57 if let Some(into_iter_item_ty
) = get_associated_type(cx
, collection_ty
, into_iterator_trait_id
, "Item");
59 if iter_item_ty
== into_iter_item_ty
;
60 if let Some(collection_snippet
) = snippet_opt(cx
, collection
.span
);
71 &format
!("unnecessary use of `{}`", method_name
),
73 // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to
74 // a `to_owned`-like function was removed, then the next suggestion may be
75 // incorrect. This is because the iterator that results from the call's removal
76 // could hold a reference to a resource that is used mutably. See
77 // https://github.com/rust-lang/rust-clippy/issues/8148.
78 let applicability
= if cloned_before_iter
{
79 Applicability
::MaybeIncorrect
81 Applicability
::MachineApplicable
83 diag
.span_suggestion(expr
.span
, "use", snippet
, applicability
);
84 for addr_of_expr
in addr_of_exprs
{
85 match addr_of_expr
.kind
{
86 ExprKind
::AddrOf(_
, _
, referent
) => {
87 let span
= addr_of_expr
.span
.with_hi(referent
.span
.lo());
88 diag
.span_suggestion(span
, "remove this `&`", "", applicability
);
101 /// Returns true if the named method is `IntoIterator::into_iter`.
102 pub fn is_into_iter(cx
: &LateContext
<'_
>, callee_def_id
: DefId
) -> bool
{
103 cx
.tcx
.lang_items().require(LangItem
::IntoIterIntoIter
) == Ok(callee_def_id
)