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