Skip to content

Commit bbed852

Browse files
committed
unnecessary_fold to its own module
1 parent 78e572c commit bbed852

File tree

2 files changed

+107
-94
lines changed

2 files changed

+107
-94
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 6 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ mod string_extend_chars;
4040
mod suspicious_map;
4141
mod uninit_assumed_init;
4242
mod unnecessary_filter_map;
43+
mod unnecessary_fold;
4344
mod unnecessary_lazy_eval;
4445
mod unwrap_used;
4546
mod useless_asref;
@@ -53,7 +54,7 @@ use if_chain::if_chain;
5354
use rustc_ast::ast;
5455
use rustc_errors::Applicability;
5556
use rustc_hir as hir;
56-
use rustc_hir::{PatKind, TraitItem, TraitItemKind};
57+
use rustc_hir::{TraitItem, TraitItemKind};
5758
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
5859
use rustc_middle::lint::in_external_macro;
5960
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
@@ -67,10 +68,9 @@ use crate::utils::eager_or_lazy::is_lazyness_candidate;
6768
use crate::utils::usage::mutated_variables;
6869
use crate::utils::{
6970
contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_expn_of,
70-
is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method,
71-
match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths, remove_blocks, return_ty,
72-
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
73-
span_lint_and_help, span_lint_and_sugg, strip_pat_refs, SpanlessEq,
71+
is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_type, meets_msrv,
72+
method_calls, method_chain_args, paths, return_ty, single_segment_path, snippet, snippet_with_applicability,
73+
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
7474
};
7575

7676
declare_clippy_lint! {
@@ -1736,7 +1736,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
17361736
["collect", "cloned"] => iter_cloned_collect::check(cx, expr, arg_lists[1]),
17371737
["as_ref"] => useless_asref::check(cx, expr, "as_ref", arg_lists[0]),
17381738
["as_mut"] => useless_asref::check(cx, expr, "as_mut", arg_lists[0]),
1739-
["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
1739+
["fold", ..] => unnecessary_fold::check(cx, expr, arg_lists[0], method_spans[0]),
17401740
["filter_map", ..] => {
17411741
unnecessary_filter_map::check(cx, expr, arg_lists[0]);
17421742
filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]);
@@ -2324,94 +2324,6 @@ fn lint_expect_fun_call(
23242324
);
23252325
}
23262326

2327-
fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) {
2328-
fn check_fold_with_op(
2329-
cx: &LateContext<'_>,
2330-
expr: &hir::Expr<'_>,
2331-
fold_args: &[hir::Expr<'_>],
2332-
fold_span: Span,
2333-
op: hir::BinOpKind,
2334-
replacement_method_name: &str,
2335-
replacement_has_args: bool,
2336-
) {
2337-
if_chain! {
2338-
// Extract the body of the closure passed to fold
2339-
if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind;
2340-
let closure_body = cx.tcx.hir().body(body_id);
2341-
let closure_expr = remove_blocks(&closure_body.value);
2342-
2343-
// Check if the closure body is of the form `acc <op> some_expr(x)`
2344-
if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind;
2345-
if bin_op.node == op;
2346-
2347-
// Extract the names of the two arguments to the closure
2348-
if let [param_a, param_b] = closure_body.params;
2349-
if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(&param_a.pat).kind;
2350-
if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(&param_b.pat).kind;
2351-
2352-
if path_to_local_id(left_expr, first_arg_id);
2353-
if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
2354-
2355-
then {
2356-
let mut applicability = Applicability::MachineApplicable;
2357-
let sugg = if replacement_has_args {
2358-
format!(
2359-
"{replacement}(|{s}| {r})",
2360-
replacement = replacement_method_name,
2361-
s = second_arg_ident,
2362-
r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
2363-
)
2364-
} else {
2365-
format!(
2366-
"{replacement}()",
2367-
replacement = replacement_method_name,
2368-
)
2369-
};
2370-
2371-
span_lint_and_sugg(
2372-
cx,
2373-
UNNECESSARY_FOLD,
2374-
fold_span.with_hi(expr.span.hi()),
2375-
// TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
2376-
"this `.fold` can be written more succinctly using another method",
2377-
"try",
2378-
sugg,
2379-
applicability,
2380-
);
2381-
}
2382-
}
2383-
}
2384-
2385-
// Check that this is a call to Iterator::fold rather than just some function called fold
2386-
if !match_trait_method(cx, expr, &paths::ITERATOR) {
2387-
return;
2388-
}
2389-
2390-
assert!(
2391-
fold_args.len() == 3,
2392-
"Expected fold_args to have three entries - the receiver, the initial value and the closure"
2393-
);
2394-
2395-
// Check if the first argument to .fold is a suitable literal
2396-
if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind {
2397-
match lit.node {
2398-
ast::LitKind::Bool(false) => {
2399-
check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true)
2400-
},
2401-
ast::LitKind::Bool(true) => {
2402-
check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true)
2403-
},
2404-
ast::LitKind::Int(0, _) => {
2405-
check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false)
2406-
},
2407-
ast::LitKind::Int(1, _) => {
2408-
check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false)
2409-
},
2410-
_ => (),
2411-
}
2412-
}
2413-
}
2414-
24152327
fn derefs_to_slice<'tcx>(
24162328
cx: &LateContext<'tcx>,
24172329
expr: &'tcx hir::Expr<'tcx>,
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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+
}

0 commit comments

Comments
 (0)