]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
merge rustc history
[rust.git] / src / tools / clippy / clippy_lints / src / methods / iter_on_single_or_empty_collections.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::snippet;
3 use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
4
5 use rustc_errors::Applicability;
6 use rustc_hir::LangItem::{OptionNone, OptionSome};
7 use rustc_hir::{Expr, ExprKind, Node};
8 use rustc_lint::LateContext;
9
10 use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
11
12 enum IterType {
13     Iter,
14     IterMut,
15     IntoIter,
16 }
17
18 impl IterType {
19     fn ref_prefix(&self) -> &'static str {
20         match self {
21             Self::Iter => "&",
22             Self::IterMut => "&mut ",
23             Self::IntoIter => "",
24         }
25     }
26 }
27
28 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
29     let item = match &recv.kind {
30         ExprKind::Array(v) if v.len() <= 1 => v.first(),
31         ExprKind::Path(p) => {
32             if is_lang_ctor(cx, p, OptionNone) {
33                 None
34             } else {
35                 return;
36             }
37         },
38         ExprKind::Call(f, some_args) if some_args.len() == 1 => {
39             if let ExprKind::Path(p) = &f.kind {
40                 if is_lang_ctor(cx, p, OptionSome) {
41                     Some(&some_args[0])
42                 } else {
43                     return;
44                 }
45             } else {
46                 return;
47             }
48         },
49         _ => return,
50     };
51     let iter_type = match method_name {
52         "iter" => IterType::Iter,
53         "iter_mut" => IterType::IterMut,
54         "into_iter" => IterType::IntoIter,
55         _ => return,
56     };
57
58     let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
59         Some((Node::Expr(parent), child_id)) => match parent.kind {
60             ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
61             ExprKind::If(_, _, _)
62             | ExprKind::Match(_, _, _)
63             | ExprKind::Closure(_)
64             | ExprKind::Ret(_)
65             | ExprKind::Break(_, _) => true,
66             _ => false,
67         },
68         Some((Node::Stmt(_) | Node::Local(_), _)) => false,
69         _ => true,
70     };
71
72     if is_unified {
73         return;
74     }
75
76     if let Some(i) = item {
77         let sugg = format!(
78             "{}::iter::once({}{})",
79             if is_no_std_crate(cx) { "core" } else { "std" },
80             iter_type.ref_prefix(),
81             snippet(cx, i.span, "...")
82         );
83         span_lint_and_sugg(
84             cx,
85             ITER_ON_SINGLE_ITEMS,
86             expr.span,
87             &format!("`{method_name}` call on a collection with only one item"),
88             "try",
89             sugg,
90             Applicability::MaybeIncorrect,
91         );
92     } else {
93         span_lint_and_sugg(
94             cx,
95             ITER_ON_EMPTY_COLLECTIONS,
96             expr.span,
97             &format!("`{method_name}` call on an empty collection"),
98             "try",
99             if is_no_std_crate(cx) {
100                 "core::iter::empty()".to_string()
101             } else {
102                 "std::iter::empty()".to_string()
103             },
104             Applicability::MaybeIncorrect,
105         );
106     }
107 }