]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / src / tools / clippy / clippy_lints / src / methods / unnecessary_fold.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::snippet_with_applicability;
3 use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs};
4 use if_chain::if_chain;
5 use rustc_ast::ast;
6 use rustc_errors::Applicability;
7 use rustc_hir as hir;
8 use rustc_hir::PatKind;
9 use rustc_lint::LateContext;
10 use rustc_span::{source_map::Span, sym};
11
12 use super::UNNECESSARY_FOLD;
13
14 pub(super) fn check(
15     cx: &LateContext<'_>,
16     expr: &hir::Expr<'_>,
17     init: &hir::Expr<'_>,
18     acc: &hir::Expr<'_>,
19     fold_span: Span,
20 ) {
21     fn check_fold_with_op(
22         cx: &LateContext<'_>,
23         expr: &hir::Expr<'_>,
24         acc: &hir::Expr<'_>,
25         fold_span: Span,
26         op: hir::BinOpKind,
27         replacement_method_name: &str,
28         replacement_has_args: bool,
29     ) {
30         if_chain! {
31             // Extract the body of the closure passed to fold
32             if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
33             let closure_body = cx.tcx.hir().body(body);
34             let closure_expr = peel_blocks(closure_body.value);
35
36             // Check if the closure body is of the form `acc <op> some_expr(x)`
37             if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
38             if bin_op.node == op;
39
40             // Extract the names of the two arguments to the closure
41             if let [param_a, param_b] = closure_body.params;
42             if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind;
43             if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind;
44
45             if path_to_local_id(left_expr, first_arg_id);
46             if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
47
48             then {
49                 let mut applicability = Applicability::MachineApplicable;
50                 let sugg = if replacement_has_args {
51                     format!(
52                         "{replacement_method_name}(|{second_arg_ident}| {r})",
53                         r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
54                     )
55                 } else {
56                     format!(
57                         "{replacement_method_name}()",
58                     )
59                 };
60
61                 span_lint_and_sugg(
62                     cx,
63                     UNNECESSARY_FOLD,
64                     fold_span.with_hi(expr.span.hi()),
65                     // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
66                     "this `.fold` can be written more succinctly using another method",
67                     "try",
68                     sugg,
69                     applicability,
70                 );
71             }
72         }
73     }
74
75     // Check that this is a call to Iterator::fold rather than just some function called fold
76     if !is_trait_method(cx, expr, sym::Iterator) {
77         return;
78     }
79
80     // Check if the first argument to .fold is a suitable literal
81     if let hir::ExprKind::Lit(ref lit) = init.kind {
82         match lit.node {
83             ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true),
84             ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true),
85             ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, "sum", false),
86             ast::LitKind::Int(1, _) => {
87                 check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false);
88             },
89             _ => (),
90         }
91     }
92 }