]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/loops/for_kv_map.rs
Merge remote-tracking branch 'upstream/master' into rustup
[rust.git] / clippy_lints / src / loops / for_kv_map.rs
1 use super::FOR_KV_MAP;
2 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
3 use clippy_utils::source::snippet;
4 use clippy_utils::sugg;
5 use clippy_utils::ty::is_type_diagnostic_item;
6 use clippy_utils::visitors::LocalUsedVisitor;
7 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
8 use rustc_lint::LateContext;
9 use rustc_middle::ty;
10 use rustc_span::sym;
11
12 /// Checks for the `FOR_KV_MAP` lint.
13 pub(super) fn check<'tcx>(
14     cx: &LateContext<'tcx>,
15     pat: &'tcx Pat<'_>,
16     arg: &'tcx Expr<'_>,
17     body: &'tcx Expr<'_>,
18     expr: &'tcx Expr<'_>,
19 ) {
20     let pat_span = pat.span;
21
22     if let PatKind::Tuple(pat, _) = pat.kind {
23         if pat.len() == 2 {
24             let arg_span = arg.span;
25             let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() {
26                 ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
27                     (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl),
28                     (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not),
29                     _ => return,
30                 },
31                 _ => return,
32             };
33             let mutbl = match mutbl {
34                 Mutability::Not => "",
35                 Mutability::Mut => "_mut",
36             };
37             let arg = match arg.kind {
38                 ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr,
39                 _ => arg,
40             };
41
42             if is_type_diagnostic_item(cx, ty, sym::hashmap_type) || is_type_diagnostic_item(cx, ty, sym::BTreeMap) {
43                 span_lint_and_then(
44                     cx,
45                     FOR_KV_MAP,
46                     expr.span,
47                     &format!("you seem to want to iterate on a map's {}s", kind),
48                     |diag| {
49                         let map = sugg::Sugg::hir(cx, arg, "map");
50                         multispan_sugg(
51                             diag,
52                             "use the corresponding method",
53                             vec![
54                                 (pat_span, snippet(cx, new_pat_span, kind).into_owned()),
55                                 (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)),
56                             ],
57                         );
58                     },
59                 );
60             }
61         }
62     }
63 }
64
65 /// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
66 fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
67     match *pat {
68         PatKind::Wild => true,
69         PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
70             !LocalUsedVisitor::new(cx, id).check_expr(body)
71         },
72         _ => false,
73     }
74 }