]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
Fix adjacent code
[rust.git] / 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_no_std_crate, is_res_lang_ctor, path_res};
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(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
29     let item = match recv.kind {
30         ExprKind::Array([]) => None,
31         ExprKind::Array([e]) => Some(e),
32         ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None,
33         ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg),
34         _ => return,
35     };
36     let iter_type = match method_name {
37         "iter" => IterType::Iter,
38         "iter_mut" => IterType::IterMut,
39         "into_iter" => IterType::IntoIter,
40         _ => return,
41     };
42
43     let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
44         Some((Node::Expr(parent), child_id)) => match parent.kind {
45             ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
46             ExprKind::If(_, _, _)
47             | ExprKind::Match(_, _, _)
48             | ExprKind::Closure(_)
49             | ExprKind::Ret(_)
50             | ExprKind::Break(_, _) => true,
51             _ => false,
52         },
53         Some((Node::Stmt(_) | Node::Local(_), _)) => false,
54         _ => true,
55     };
56
57     if is_unified {
58         return;
59     }
60
61     if let Some(i) = item {
62         let sugg = format!(
63             "{}::iter::once({}{})",
64             if is_no_std_crate(cx) { "core" } else { "std" },
65             iter_type.ref_prefix(),
66             snippet(cx, i.span, "...")
67         );
68         span_lint_and_sugg(
69             cx,
70             ITER_ON_SINGLE_ITEMS,
71             expr.span,
72             &format!("`{method_name}` call on a collection with only one item"),
73             "try",
74             sugg,
75             Applicability::MaybeIncorrect,
76         );
77     } else {
78         span_lint_and_sugg(
79             cx,
80             ITER_ON_EMPTY_COLLECTIONS,
81             expr.span,
82             &format!("`{method_name}` call on an empty collection"),
83             "try",
84             if is_no_std_crate(cx) {
85                 "core::iter::empty()".to_string()
86             } else {
87                 "std::iter::empty()".to_string()
88             },
89             Applicability::MaybeIncorrect,
90         );
91     }
92 }