-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathiter_kv_map.rs
76 lines (74 loc) · 2.95 KB
/
iter_kv_map.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use super::ITER_KV_MAP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::pat_is_wild;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir::{Body, Expr, ExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_span::sym;
/// lint use of:
///
/// - `hashmap.iter().map(|(_, v)| v)`
/// - `hashmap.into_iter().map(|(_, v)| v)`
///
/// on `HashMaps` and `BTreeMaps` in std
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
map_type: &'tcx str, // iter / into_iter
expr: &'tcx Expr<'tcx>, // .iter().map(|(_, v_| v))
recv: &'tcx Expr<'tcx>, // hashmap
m_arg: &'tcx Expr<'tcx>, // |(_, v)| v
msrv: Msrv,
) {
if map_type == "into_iter" && !msrv.meets(cx, msrvs::INTO_KEYS) {
return;
}
if !expr.span.from_expansion()
&& let ExprKind::Closure(c) = m_arg.kind
&& let Body {
params: [p],
value: body_expr,
} = cx.tcx.hir_body(c.body)
&& let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind
&& let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) {
(key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value),
(PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key),
_ => return,
}
&& let ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs()
&& (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap))
{
let mut applicability = rustc_errors::Applicability::MachineApplicable;
let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability);
let into_prefix = if map_type == "into_iter" { "into_" } else { "" };
if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind
&& let [local_ident] = path.segments
&& local_ident.ident.as_str() == bound_ident.as_str()
{
span_lint_and_sugg(
cx,
ITER_KV_MAP,
expr.span,
format!("iterating on a map's {replacement_kind}s"),
"try",
format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"),
applicability,
);
} else {
span_lint_and_sugg(
cx,
ITER_KV_MAP,
expr.span,
format!("iterating on a map's {replacement_kind}s"),
"try",
format!(
"{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {})",
annotation.prefix_str(),
snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)
),
applicability,
);
}
}
}