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