@@ -476,6 +476,32 @@ declare_clippy_lint! {
476
476
"using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
477
477
}
478
478
479
+ declare_clippy_lint ! {
480
+ /// **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply
481
+ /// as `find_map(_)`.
482
+ ///
483
+ /// **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and
484
+ /// less performant.
485
+ ///
486
+ /// **Known problems:** None.
487
+ ///
488
+ /// **Example:**
489
+ /// Bad:
490
+ /// ```rust
491
+ /// (0_i32..10)
492
+ /// .find(|n| n.checked_add(1).is_some())
493
+ /// .map(|n| n.checked_add(1).unwrap());
494
+ /// ```
495
+ ///
496
+ /// Good:
497
+ /// ```rust
498
+ /// (0_i32..10).find_map(|n| n.checked_add(1));
499
+ /// ```
500
+ pub MANUAL_FIND_MAP ,
501
+ complexity,
502
+ "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
503
+ }
504
+
479
505
declare_clippy_lint ! {
480
506
/// **What it does:** Checks for usage of `_.filter_map(_).next()`.
481
507
///
@@ -1470,6 +1496,7 @@ impl_lint_pass!(Methods => [
1470
1496
SKIP_WHILE_NEXT ,
1471
1497
FILTER_MAP ,
1472
1498
MANUAL_FILTER_MAP ,
1499
+ MANUAL_FIND_MAP ,
1473
1500
FILTER_MAP_NEXT ,
1474
1501
FLAT_MAP_IDENTITY ,
1475
1502
FIND_MAP ,
@@ -1536,10 +1563,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
1536
1563
[ "next" , "filter" ] => lint_filter_next ( cx, expr, arg_lists[ 1 ] ) ,
1537
1564
[ "next" , "skip_while" ] => lint_skip_while_next ( cx, expr, arg_lists[ 1 ] ) ,
1538
1565
[ "next" , "iter" ] => lint_iter_next ( cx, expr, arg_lists[ 1 ] ) ,
1539
- [ "map" , "filter" ] => lint_filter_map ( cx, expr) ,
1566
+ [ "map" , "filter" ] => lint_filter_map ( cx, expr, false ) ,
1540
1567
[ "map" , "filter_map" ] => lint_filter_map_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1541
1568
[ "next" , "filter_map" ] => lint_filter_map_next ( cx, expr, arg_lists[ 1 ] , self . msrv . as_ref ( ) ) ,
1542
- [ "map" , "find" ] => lint_find_map ( cx, expr, arg_lists [ 1 ] , arg_lists [ 0 ] ) ,
1569
+ [ "map" , "find" ] => lint_filter_map ( cx, expr, true ) ,
1543
1570
[ "flat_map" , "filter" ] => lint_filter_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1544
1571
[ "flat_map" , "filter_map" ] => lint_filter_map_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1545
1572
[ "flat_map" , ..] => lint_flat_map_identity ( cx, expr, arg_lists[ 0 ] , method_spans[ 0 ] ) ,
@@ -2983,12 +3010,12 @@ fn lint_skip_while_next<'tcx>(
2983
3010
}
2984
3011
}
2985
3012
2986
- /// lint use of `filter().map()` for `Iterators`
2987
- fn lint_filter_map < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
3013
+ /// lint use of `filter().map()` or `find().map()` for `Iterators`
3014
+ fn lint_filter_map < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > , is_find : bool ) {
2988
3015
if_chain ! {
2989
3016
if let ExprKind :: MethodCall ( _, _, [ map_recv, map_arg] , map_span) = expr. kind;
2990
3017
if let ExprKind :: MethodCall ( _, _, [ _, filter_arg] , filter_span) = map_recv. kind;
2991
- if match_trait_method( cx, expr , & paths:: ITERATOR ) ;
3018
+ if match_trait_method( cx, map_recv , & paths:: ITERATOR ) ;
2992
3019
2993
3020
// filter(|x| ...is_some())...
2994
3021
if let ExprKind :: Closure ( _, _, filter_body_id, ..) = filter_arg. kind;
@@ -3045,10 +3072,16 @@ fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
3045
3072
if SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg) ;
3046
3073
then {
3047
3074
let span = filter_span. to( map_span) ;
3048
- let msg = "`filter(..).map(..)` can be simplified as `filter_map(..)`" ;
3075
+ let ( filter_name, lint) = if is_find {
3076
+ ( "find" , MANUAL_FIND_MAP )
3077
+ } else {
3078
+ ( "filter" , MANUAL_FILTER_MAP )
3079
+ } ;
3080
+ let msg = format!( "`{}(..).map(..)` can be simplified as `{0}_map(..)`" , filter_name) ;
3049
3081
let to_opt = if is_result { ".ok()" } else { "" } ;
3050
- let sugg = format!( "filter_map(|{}| {}{})" , map_param_ident, snippet( cx, map_arg. span, ".." ) , to_opt) ;
3051
- span_lint_and_sugg( cx, MANUAL_FILTER_MAP , span, msg, "try" , sugg, Applicability :: MachineApplicable ) ;
3082
+ let sugg = format!( "{}_map(|{}| {}{})" , filter_name, map_param_ident,
3083
+ snippet( cx, map_arg. span, ".." ) , to_opt) ;
3084
+ span_lint_and_sugg( cx, lint, span, & msg, "try" , sugg, Applicability :: MachineApplicable ) ;
3052
3085
}
3053
3086
}
3054
3087
}
@@ -3087,21 +3120,6 @@ fn lint_filter_map_next<'tcx>(
3087
3120
}
3088
3121
}
3089
3122
3090
- /// lint use of `find().map()` for `Iterators`
3091
- fn lint_find_map < ' tcx > (
3092
- cx : & LateContext < ' tcx > ,
3093
- expr : & ' tcx hir:: Expr < ' _ > ,
3094
- _find_args : & ' tcx [ hir:: Expr < ' _ > ] ,
3095
- map_args : & ' tcx [ hir:: Expr < ' _ > ] ,
3096
- ) {
3097
- // lint if caller of `.filter().map()` is an Iterator
3098
- if match_trait_method ( cx, & map_args[ 0 ] , & paths:: ITERATOR ) {
3099
- let msg = "called `find(..).map(..)` on an `Iterator`" ;
3100
- let hint = "this is more succinctly expressed by calling `.find_map(..)` instead" ;
3101
- span_lint_and_help ( cx, FIND_MAP , expr. span , msg, None , hint) ;
3102
- }
3103
- }
3104
-
3105
3123
/// lint use of `filter_map().map()` for `Iterators`
3106
3124
fn lint_filter_map_map < ' tcx > (
3107
3125
cx : & LateContext < ' tcx > ,
0 commit comments