@@ -477,6 +477,32 @@ declare_clippy_lint! {
477
477
"using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
478
478
}
479
479
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
+
480
506
declare_clippy_lint ! {
481
507
/// **What it does:** Checks for usage of `_.filter_map(_).next()`.
482
508
///
@@ -1501,6 +1527,7 @@ impl_lint_pass!(Methods => [
1501
1527
SKIP_WHILE_NEXT ,
1502
1528
FILTER_MAP ,
1503
1529
MANUAL_FILTER_MAP ,
1530
+ MANUAL_FIND_MAP ,
1504
1531
FILTER_MAP_NEXT ,
1505
1532
FLAT_MAP_IDENTITY ,
1506
1533
FIND_MAP ,
@@ -1568,10 +1595,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
1568
1595
[ "next" , "filter" ] => lint_filter_next ( cx, expr, arg_lists[ 1 ] ) ,
1569
1596
[ "next" , "skip_while" ] => lint_skip_while_next ( cx, expr, arg_lists[ 1 ] ) ,
1570
1597
[ "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 ) ,
1572
1599
[ "map" , "filter_map" ] => lint_filter_map_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1573
1600
[ "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 ) ,
1575
1602
[ "flat_map" , "filter" ] => lint_filter_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1576
1603
[ "flat_map" , "filter_map" ] => lint_filter_map_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1577
1604
[ "flat_map" , ..] => lint_flat_map_identity ( cx, expr, arg_lists[ 0 ] , method_spans[ 0 ] ) ,
@@ -3016,12 +3043,12 @@ fn lint_skip_while_next<'tcx>(
3016
3043
}
3017
3044
}
3018
3045
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 ) {
3021
3048
if_chain ! {
3022
3049
if let ExprKind :: MethodCall ( _, _, [ map_recv, map_arg] , map_span) = expr. kind;
3023
3050
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 ) ;
3025
3052
3026
3053
// filter(|x| ...is_some())...
3027
3054
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<'_>) {
3078
3105
if SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg) ;
3079
3106
then {
3080
3107
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) ;
3082
3114
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 ) ;
3085
3118
}
3086
3119
}
3087
3120
}
@@ -3120,21 +3153,6 @@ fn lint_filter_map_next<'tcx>(
3120
3153
}
3121
3154
}
3122
3155
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
-
3138
3156
/// lint use of `filter_map().map()` for `Iterators`
3139
3157
fn lint_filter_map_map < ' tcx > (
3140
3158
cx : & LateContext < ' tcx > ,
0 commit comments