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, remove_blocks, strip_pat_refs};
4 use if_chain::if_chain;
6 use rustc_errors::Applicability;
8 use rustc_hir::PatKind;
9 use rustc_lint::LateContext;
10 use rustc_span::{source_map::Span, sym};
12 use super::UNNECESSARY_FOLD;
14 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) {
15 fn check_fold_with_op(
18 fold_args: &[hir::Expr<'_>],
21 replacement_method_name: &str,
22 replacement_has_args: bool,
25 // Extract the body of the closure passed to fold
26 if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind;
27 let closure_body = cx.tcx.hir().body(body_id);
28 let closure_expr = remove_blocks(&closure_body.value);
30 // Check if the closure body is of the form `acc <op> some_expr(x)`
31 if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind;
34 // Extract the names of the two arguments to the closure
35 if let [param_a, param_b] = closure_body.params;
36 if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(¶m_a.pat).kind;
37 if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(¶m_b.pat).kind;
39 if path_to_local_id(left_expr, first_arg_id);
40 if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
43 let mut applicability = Applicability::MachineApplicable;
44 let sugg = if replacement_has_args {
46 "{replacement}(|{s}| {r})",
47 replacement = replacement_method_name,
49 r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
54 replacement = replacement_method_name,
61 fold_span.with_hi(expr.span.hi()),
62 // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
63 "this `.fold` can be written more succinctly using another method",
72 // Check that this is a call to Iterator::fold rather than just some function called fold
73 if !is_trait_method(cx, expr, sym::Iterator) {
79 "Expected fold_args to have three entries - the receiver, the initial value and the closure"
82 // Check if the first argument to .fold is a suitable literal
83 if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind {
85 ast::LitKind::Bool(false) => {
86 check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true)
88 ast::LitKind::Bool(true) => {
89 check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true)
91 ast::LitKind::Int(0, _) => {
92 check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false)
94 ast::LitKind::Int(1, _) => {
95 check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false)