]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
Merge commit '7248d06384c6a90de58c04c1f46be88821278d8b' into sync-from-clippy
[rust.git] / src / tools / clippy / clippy_lints / src / methods / iter_kv_map.rs
1 #![allow(unused_imports)]
2
3 use super::ITER_KV_MAP;
4 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_sugg, span_lint_and_then};
5 use clippy_utils::source::{snippet, snippet_with_applicability};
6 use clippy_utils::sugg;
7 use clippy_utils::ty::is_type_diagnostic_item;
8 use clippy_utils::visitors::is_local_used;
9 use rustc_hir::{BindingAnnotation, Body, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
10 use rustc_lint::{LateContext, LintContext};
11 use rustc_middle::ty;
12 use rustc_span::sym;
13 use rustc_span::Span;
14
15 /// lint use of:
16 /// - `hashmap.iter().map(|(_, v)| v)`
17 /// - `hashmap.into_iter().map(|(_, v)| v)`
18 /// on `HashMaps` and `BTreeMaps` in std
19
20 pub(super) fn check<'tcx>(
21     cx: &LateContext<'tcx>,
22     map_type: &'tcx str,     // iter / into_iter
23     expr: &'tcx Expr<'tcx>,  // .iter().map(|(_, v_| v))
24     recv: &'tcx Expr<'tcx>,  // hashmap
25     m_arg: &'tcx Expr<'tcx>, // |(_, v)| v
26 ) {
27     if_chain! {
28         if !expr.span.from_expansion();
29         if let ExprKind::Closure(c) = m_arg.kind;
30         if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body);
31         if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind;
32
33         let (replacement_kind, binded_ident) = match (&key_pat.kind, &val_pat.kind) {
34             (key, PatKind::Binding(_, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", value),
35             (PatKind::Binding(_, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", key),
36             _ => return,
37         };
38
39         let ty = cx.typeck_results().expr_ty(recv);
40         if is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap);
41
42         then {
43             let mut applicability = rustc_errors::Applicability::MachineApplicable;
44             let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability);
45             let into_prefix = if map_type == "into_iter" {"into_"} else {""};
46
47             if_chain! {
48                 if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind;
49                 if let [local_ident] = path.segments;
50                 if local_ident.ident.as_str() == binded_ident.as_str();
51
52                 then {
53                     span_lint_and_sugg(
54                         cx,
55                         ITER_KV_MAP,
56                         expr.span,
57                         &format!("iterating on a map's {}s", replacement_kind),
58                         "try",
59                         format!("{}.{}{}s()", recv_snippet, into_prefix, replacement_kind),
60                         applicability,
61                     );
62                 } else {
63                     span_lint_and_sugg(
64                         cx,
65                         ITER_KV_MAP,
66                         expr.span,
67                         &format!("iterating on a map's {}s", replacement_kind),
68                         "try",
69                         format!("{}.{}{}s().map(|{}| {})", recv_snippet, into_prefix, replacement_kind, binded_ident,
70                             snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)),
71                         applicability,
72                     );
73                 }
74             }
75         }
76     }
77 }
78
79 /// Returns `true` if the pattern is a `PatWild`, or is an ident prefixed with `_`
80 /// that is not locally used.
81 fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
82     match *pat {
83         PatKind::Wild => true,
84         PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id),
85         _ => false,
86     }
87 }