]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs
Merge commit 'fdb84cbfd25908df5683f8f62388f663d9260e39' into clippyup
[rust.git] / src / tools / clippy / 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::ForLoop::hir(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::hir(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                 let suggest = if start_idx == 0 {
38                     format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability))
39                 } else {
40                     format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx)
41                 };
42                 span_lint_and_sugg(
43                     cx,
44                     ITER_NEXT_SLICE,
45                     expr.span,
46                     "using `.iter().next()` on a Slice without end index",
47                     "try calling",
48                     suggest,
49                     applicability,
50                 );
51             }
52         }
53     } else if is_vec_or_array(cx, caller_expr) {
54         // caller is a Vec or an Array
55         let mut applicability = Applicability::MachineApplicable;
56         span_lint_and_sugg(
57             cx,
58             ITER_NEXT_SLICE,
59             expr.span,
60             "using `.iter().next()` on an array",
61             "try calling",
62             format!(
63                 "{}.first()",
64                 snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability)
65             ),
66             applicability,
67         );
68     }
69 }
70
71 fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
72     is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec)
73         || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
74 }