]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
Merge commit 'fdb84cbfd25908df5683f8f62388f663d9260e39' into clippyup
[rust.git] / src / tools / clippy / clippy_lints / src / methods / unnecessary_iter_cloned.rs
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};
11
12 use super::UNNECESSARY_TO_OWNED;
13
14 pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
15     if_chain! {
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);
19         then {
20             check_for_loop_iter(cx, parent, method_name, receiver, false)
21         } else {
22             false
23         }
24     }
25 }
26
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(
32     cx: &LateContext<'_>,
33     expr: &Expr<'_>,
34     method_name: Symbol,
35     receiver: &Expr<'_>,
36     cloned_before_iter: bool,
37 ) -> bool {
38     if_chain! {
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);
44         then {
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;
48
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);
53
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");
58
59                 if iter_item_ty == into_iter_item_ty;
60                 if let Some(collection_snippet) = snippet_opt(cx, collection.span);
61                 then {
62                     collection_snippet
63                 } else {
64                     receiver_snippet
65                 }
66             };
67             span_lint_and_then(
68                 cx,
69                 UNNECESSARY_TO_OWNED,
70                 expr.span,
71                 &format!("unnecessary use of `{}`", method_name),
72                 |diag| {
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
80                     } else {
81                         Applicability::MachineApplicable
82                     };
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);
89                             }
90                             _ => unreachable!(),
91                         }
92                     }
93                 }
94             );
95             return true;
96         }
97     }
98     false
99 }
100
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)
104 }