]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods/iter_next_slice.rs
Auto merge of #7546 - mgeier:patch-1, r=giraffate
[rust.git] / clippy_lints / src / methods / iter_next_slice.rs
1 use super::utils::derefs_to_slice;
2 use clippy_utils::diagnostics::span_lint_and_sugg;
3 use clippy_utils::source::snippet_with_applicability;
4 use clippy_utils::ty::is_type_diagnostic_item;
5 use clippy_utils::{get_parent_expr, higher};
6 use if_chain::if_chain;
7 use rustc_ast::ast;
8 use rustc_errors::Applicability;
9 use rustc_hir as hir;
10 use rustc_lint::LateContext;
11 use rustc_middle::ty;
12 use rustc_span::symbol::sym;
13
14 use super::ITER_NEXT_SLICE;
15
16 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, caller_expr: &'tcx hir::Expr<'_>) {
17     // Skip lint if the `iter().next()` expression is a for loop argument,
18     // since it is already covered by `&loops::ITER_NEXT_LOOP`
19     let mut parent_expr_opt = get_parent_expr(cx, expr);
20     while let Some(parent_expr) = parent_expr_opt {
21         if higher::for_loop(parent_expr).is_some() {
22             return;
23         }
24         parent_expr_opt = get_parent_expr(cx, parent_expr);
25     }
26
27     if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() {
28         // caller is a Slice
29         if_chain! {
30             if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind;
31             if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
32                 = higher::range(index_expr);
33             if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
34             if let ast::LitKind::Int(start_idx, _) = start_lit.node;
35             then {
36                 let mut applicability = Applicability::MachineApplicable;
37                 span_lint_and_sugg(
38                     cx,
39                     ITER_NEXT_SLICE,
40                     expr.span,
41                     "using `.iter().next()` on a Slice without end index",
42                     "try calling",
43                     format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx),
44                     applicability,
45                 );
46             }
47         }
48     } else if is_vec_or_array(cx, caller_expr) {
49         // caller is a Vec or an Array
50         let mut applicability = Applicability::MachineApplicable;
51         span_lint_and_sugg(
52             cx,
53             ITER_NEXT_SLICE,
54             expr.span,
55             "using `.iter().next()` on an array",
56             "try calling",
57             format!(
58                 "{}.get(0)",
59                 snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability)
60             ),
61             applicability,
62         );
63     }
64 }
65
66 fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
67     is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type)
68         || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
69 }