1 use super::utils::clone_or_copy_needed;
2 use clippy_utils::diagnostics::span_lint;
3 use clippy_utils::ty::is_copy;
4 use clippy_utils::usage::mutated_variables;
5 use clippy_utils::visitors::{for_each_expr, Descend};
6 use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
7 use core::ops::ControlFlow;
9 use rustc_hir::LangItem::{OptionNone, OptionSome};
10 use rustc_lint::LateContext;
14 use super::UNNECESSARY_FILTER_MAP;
15 use super::UNNECESSARY_FIND_MAP;
17 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) {
18 if !is_trait_method(cx, expr, sym::Iterator) {
22 if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
23 let body = cx.tcx.hir().body(body);
24 let arg_id = body.params[0].pat.hir_id;
25 let mutates_arg = mutated_variables(body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
26 let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, body.value);
28 let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
30 let _: Option<!> = for_each_expr(body.value, |e| {
31 if let hir::ExprKind::Ret(Some(e)) = &e.kind {
32 let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e);
33 found_mapping |= found_mapping_res;
34 found_filtering |= found_filtering_res;
35 ControlFlow::Continue(Descend::No)
37 ControlFlow::Continue(Descend::Yes)
41 let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
42 let sugg = if !found_filtering {
43 if name == "filter_map" { "map" } else { "map(..).next()" }
44 } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) {
45 match cx.typeck_results().expr_ty(body.value).kind() {
47 if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) =>
49 if name == "filter_map" { "filter" } else { "find" }
58 if name == "filter_map" {
59 UNNECESSARY_FILTER_MAP
64 &format!("this `.{name}` can be written more simply using `.{sugg}`"),
69 // returns (found_mapping, found_filtering)
70 fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) {
72 hir::ExprKind::Call(func, args) => {
73 if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) {
74 if path_to_local_id(&args[0], arg_id) {
75 return (false, false);
81 hir::ExprKind::Block(block, _) => block
84 .map_or((false, false), |expr| check_expression(cx, arg_id, expr)),
85 hir::ExprKind::Match(_, arms, _) => {
86 let mut found_mapping = false;
87 let mut found_filtering = false;
89 let (m, f) = check_expression(cx, arg_id, arm.body);
93 (found_mapping, found_filtering)
95 // There must be an else_arm or there will be a type error
96 hir::ExprKind::If(_, if_arm, Some(else_arm)) => {
97 let if_check = check_expression(cx, arg_id, if_arm);
98 let else_check = check_expression(cx, arg_id, else_arm);
99 (if_check.0 | else_check.0, if_check.1 | else_check.1)
101 hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => {