@@ -477,6 +477,32 @@ declare_clippy_lint! {
477477 "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
478478}
479479
480+ declare_clippy_lint ! {
481+ /// **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply
482+ /// as `find_map(_)`.
483+ ///
484+ /// **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and
485+ /// less performant.
486+ ///
487+ /// **Known problems:** None.
488+ ///
489+ /// **Example:**
490+ /// Bad:
491+ /// ```rust
492+ /// (0_i32..10)
493+ /// .find(|n| n.checked_add(1).is_some())
494+ /// .map(|n| n.checked_add(1).unwrap());
495+ /// ```
496+ ///
497+ /// Good:
498+ /// ```rust
499+ /// (0_i32..10).find_map(|n| n.checked_add(1));
500+ /// ```
501+ pub MANUAL_FIND_MAP ,
502+ complexity,
503+ "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
504+ }
505+
480506declare_clippy_lint ! {
481507 /// **What it does:** Checks for usage of `_.filter_map(_).next()`.
482508 ///
@@ -1501,6 +1527,7 @@ impl_lint_pass!(Methods => [
15011527 SKIP_WHILE_NEXT ,
15021528 FILTER_MAP ,
15031529 MANUAL_FILTER_MAP ,
1530+ MANUAL_FIND_MAP ,
15041531 FILTER_MAP_NEXT ,
15051532 FLAT_MAP_IDENTITY ,
15061533 FIND_MAP ,
@@ -1568,10 +1595,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
15681595 [ "next" , "filter" ] => lint_filter_next ( cx, expr, arg_lists[ 1 ] ) ,
15691596 [ "next" , "skip_while" ] => lint_skip_while_next ( cx, expr, arg_lists[ 1 ] ) ,
15701597 [ "next" , "iter" ] => lint_iter_next ( cx, expr, arg_lists[ 1 ] ) ,
1571- [ "map" , "filter" ] => lint_filter_map ( cx, expr) ,
1598+ [ "map" , "filter" ] => lint_filter_map ( cx, expr, false ) ,
15721599 [ "map" , "filter_map" ] => lint_filter_map_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
15731600 [ "next" , "filter_map" ] => lint_filter_map_next ( cx, expr, arg_lists[ 1 ] , self . msrv . as_ref ( ) ) ,
1574- [ "map" , "find" ] => lint_find_map ( cx, expr, arg_lists [ 1 ] , arg_lists [ 0 ] ) ,
1601+ [ "map" , "find" ] => lint_filter_map ( cx, expr, true ) ,
15751602 [ "flat_map" , "filter" ] => lint_filter_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
15761603 [ "flat_map" , "filter_map" ] => lint_filter_map_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
15771604 [ "flat_map" , ..] => lint_flat_map_identity ( cx, expr, arg_lists[ 0 ] , method_spans[ 0 ] ) ,
@@ -3016,12 +3043,12 @@ fn lint_skip_while_next<'tcx>(
30163043 }
30173044}
30183045
3019- /// lint use of `filter().map()` for `Iterators`
3020- fn lint_filter_map < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
3046+ /// lint use of `filter().map()` or `find().map()` for `Iterators`
3047+ fn lint_filter_map < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > , is_find : bool ) {
30213048 if_chain ! {
30223049 if let ExprKind :: MethodCall ( _, _, [ map_recv, map_arg] , map_span) = expr. kind;
30233050 if let ExprKind :: MethodCall ( _, _, [ _, filter_arg] , filter_span) = map_recv. kind;
3024- if match_trait_method( cx, expr , & paths:: ITERATOR ) ;
3051+ if match_trait_method( cx, map_recv , & paths:: ITERATOR ) ;
30253052
30263053 // filter(|x| ...is_some())...
30273054 if let ExprKind :: Closure ( _, _, filter_body_id, ..) = filter_arg. kind;
@@ -3078,10 +3105,16 @@ fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
30783105 if SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg) ;
30793106 then {
30803107 let span = filter_span. to( map_span) ;
3081- let msg = "`filter(..).map(..)` can be simplified as `filter_map(..)`" ;
3108+ let ( filter_name, lint) = if is_find {
3109+ ( "find" , MANUAL_FIND_MAP )
3110+ } else {
3111+ ( "filter" , MANUAL_FILTER_MAP )
3112+ } ;
3113+ let msg = format!( "`{}(..).map(..)` can be simplified as `{0}_map(..)`" , filter_name) ;
30823114 let to_opt = if is_result { ".ok()" } else { "" } ;
3083- let sugg = format!( "filter_map(|{}| {}{})" , map_param_ident, snippet( cx, map_arg. span, ".." ) , to_opt) ;
3084- span_lint_and_sugg( cx, MANUAL_FILTER_MAP , span, msg, "try" , sugg, Applicability :: MachineApplicable ) ;
3115+ let sugg = format!( "{}_map(|{}| {}{})" , filter_name, map_param_ident,
3116+ snippet( cx, map_arg. span, ".." ) , to_opt) ;
3117+ span_lint_and_sugg( cx, lint, span, & msg, "try" , sugg, Applicability :: MachineApplicable ) ;
30853118 }
30863119 }
30873120}
@@ -3120,21 +3153,6 @@ fn lint_filter_map_next<'tcx>(
31203153 }
31213154}
31223155
3123- /// lint use of `find().map()` for `Iterators`
3124- fn lint_find_map < ' tcx > (
3125- cx : & LateContext < ' tcx > ,
3126- expr : & ' tcx hir:: Expr < ' _ > ,
3127- _find_args : & ' tcx [ hir:: Expr < ' _ > ] ,
3128- map_args : & ' tcx [ hir:: Expr < ' _ > ] ,
3129- ) {
3130- // lint if caller of `.filter().map()` is an Iterator
3131- if match_trait_method ( cx, & map_args[ 0 ] , & paths:: ITERATOR ) {
3132- let msg = "called `find(..).map(..)` on an `Iterator`" ;
3133- let hint = "this is more succinctly expressed by calling `.find_map(..)` instead" ;
3134- span_lint_and_help ( cx, FIND_MAP , expr. span , msg, None , hint) ;
3135- }
3136- }
3137-
31383156/// lint use of `filter_map().map()` for `Iterators`
31393157fn lint_filter_map_map < ' tcx > (
31403158 cx : & LateContext < ' tcx > ,
0 commit comments