]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
Rollup merge of #99244 - gthb:doc-improve-iterator-scan, r=m-ou-se
[rust.git] / src / tools / clippy / clippy_lints / src / loops / single_element_loop.rs
1 use super::SINGLE_ELEMENT_LOOP;
2 use clippy_utils::diagnostics::span_lint_and_sugg;
3 use clippy_utils::source::{indent_of, snippet_with_applicability};
4 use if_chain::if_chain;
5 use rustc_ast::util::parser::PREC_PREFIX;
6 use rustc_ast::Mutability;
7 use rustc_errors::Applicability;
8 use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat};
9 use rustc_lint::LateContext;
10 use rustc_span::edition::Edition;
11
12 pub(super) fn check<'tcx>(
13     cx: &LateContext<'tcx>,
14     pat: &'tcx Pat<'_>,
15     arg: &'tcx Expr<'_>,
16     body: &'tcx Expr<'_>,
17     expr: &'tcx Expr<'_>,
18 ) {
19     let (arg_expression, prefix) = match arg.kind {
20         ExprKind::AddrOf(
21             BorrowKind::Ref,
22             Mutability::Not,
23             Expr {
24                 kind: ExprKind::Array([arg]),
25                 ..
26             },
27         ) => (arg, "&"),
28         ExprKind::AddrOf(
29             BorrowKind::Ref,
30             Mutability::Mut,
31             Expr {
32                 kind: ExprKind::Array([arg]),
33                 ..
34             },
35         ) => (arg, "&mut "),
36         ExprKind::MethodCall(
37             method,
38             Expr {
39                 kind: ExprKind::Array([arg]),
40                 ..
41             },
42             [],
43             _,
44         ) if method.ident.name == rustc_span::sym::iter => (arg, "&"),
45         ExprKind::MethodCall(
46             method,
47             Expr {
48                 kind: ExprKind::Array([arg]),
49                 ..
50             },
51             [],
52             _,
53         ) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "),
54         ExprKind::MethodCall(
55             method,
56             Expr {
57                 kind: ExprKind::Array([arg]),
58                 ..
59             },
60             [],
61             _,
62         ) if method.ident.name == rustc_span::sym::into_iter => (arg, ""),
63         // Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise.
64         ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""),
65         _ => return,
66     };
67     if_chain! {
68         if let ExprKind::Block(block, _) = body.kind;
69         if !block.stmts.is_empty();
70         then {
71             let mut applicability = Applicability::MachineApplicable;
72             let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
73             let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
74             let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
75             block_str.remove(0);
76             block_str.pop();
77             let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
78
79             // Reference iterator from `&(mut) []` or `[].iter(_mut)()`.
80             if !prefix.is_empty() && (
81                 // Precedence of internal expression is less than or equal to precedence of `&expr`.
82                 arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
83             ) {
84                 arg_snip = format!("({arg_snip})").into();
85             }
86
87             span_lint_and_sugg(
88                 cx,
89                 SINGLE_ELEMENT_LOOP,
90                 expr.span,
91                 "for loop over a single element",
92                 "try",
93                 format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
94                 applicability,
95             )
96         }
97     }
98 }