@@ -3,6 +3,7 @@ mod bytes_nth;
3
3
mod clone_on_ref_ptr;
4
4
mod expect_used;
5
5
mod filetype_is_file;
6
+ mod filter_map;
6
7
mod filter_map_identity;
7
8
mod filter_next;
8
9
mod from_iter_instead_of_collect;
@@ -43,7 +44,7 @@ use if_chain::if_chain;
43
44
use rustc_ast:: ast;
44
45
use rustc_errors:: Applicability ;
45
46
use rustc_hir as hir;
46
- use rustc_hir:: { Expr , ExprKind , PatKind , TraitItem , TraitItemKind , UnOp } ;
47
+ use rustc_hir:: { PatKind , TraitItem , TraitItemKind } ;
47
48
use rustc_lint:: { LateContext , LateLintPass , Lint , LintContext } ;
48
49
use rustc_middle:: lint:: in_external_macro;
49
50
use rustc_middle:: ty:: { self , TraitRef , Ty , TyS } ;
@@ -1698,10 +1699,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
1698
1699
[ "next" , "filter" ] => filter_next:: check ( cx, expr, arg_lists[ 1 ] ) ,
1699
1700
[ "next" , "skip_while" ] => skip_while_next:: check ( cx, expr, arg_lists[ 1 ] ) ,
1700
1701
[ "next" , "iter" ] => iter_next_slice:: check ( cx, expr, arg_lists[ 1 ] ) ,
1701
- [ "map" , "filter" ] => lint_filter_map ( cx, expr, false ) ,
1702
+ [ "map" , "filter" ] => filter_map :: check ( cx, expr, false ) ,
1702
1703
[ "map" , "filter_map" ] => lint_filter_map_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1703
1704
[ "next" , "filter_map" ] => lint_filter_map_next ( cx, expr, arg_lists[ 1 ] , self . msrv . as_ref ( ) ) ,
1704
- [ "map" , "find" ] => lint_filter_map ( cx, expr, true ) ,
1705
+ [ "map" , "find" ] => filter_map :: check ( cx, expr, true ) ,
1705
1706
[ "flat_map" , "filter" ] => lint_filter_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1706
1707
[ "flat_map" , "filter_map" ] => lint_filter_map_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1707
1708
[ "flat_map" , ..] => lint_flat_map_identity ( cx, expr, arg_lists[ 0 ] , method_spans[ 0 ] ) ,
@@ -2750,80 +2751,6 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
2750
2751
) ;
2751
2752
}
2752
2753
2753
- /// lint use of `filter().map()` or `find().map()` for `Iterators`
2754
- fn lint_filter_map < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > , is_find : bool ) {
2755
- if_chain ! {
2756
- if let ExprKind :: MethodCall ( _, _, [ map_recv, map_arg] , map_span) = expr. kind;
2757
- if let ExprKind :: MethodCall ( _, _, [ _, filter_arg] , filter_span) = map_recv. kind;
2758
- if match_trait_method( cx, map_recv, & paths:: ITERATOR ) ;
2759
-
2760
- // filter(|x| ...is_some())...
2761
- if let ExprKind :: Closure ( _, _, filter_body_id, ..) = filter_arg. kind;
2762
- let filter_body = cx. tcx. hir( ) . body( filter_body_id) ;
2763
- if let [ filter_param] = filter_body. params;
2764
- // optional ref pattern: `filter(|&x| ..)`
2765
- let ( filter_pat, is_filter_param_ref) = if let PatKind :: Ref ( ref_pat, _) = filter_param. pat. kind {
2766
- ( ref_pat, true )
2767
- } else {
2768
- ( filter_param. pat, false )
2769
- } ;
2770
- // closure ends with is_some() or is_ok()
2771
- if let PatKind :: Binding ( _, filter_param_id, _, None ) = filter_pat. kind;
2772
- if let ExprKind :: MethodCall ( path, _, [ filter_arg] , _) = filter_body. value. kind;
2773
- if let Some ( opt_ty) = cx. typeck_results( ) . expr_ty( filter_arg) . ty_adt_def( ) ;
2774
- if let Some ( is_result) = if cx. tcx. is_diagnostic_item( sym:: option_type, opt_ty. did) {
2775
- Some ( false )
2776
- } else if cx. tcx. is_diagnostic_item( sym:: result_type, opt_ty. did) {
2777
- Some ( true )
2778
- } else {
2779
- None
2780
- } ;
2781
- if path. ident. name. as_str( ) == if is_result { "is_ok" } else { "is_some" } ;
2782
-
2783
- // ...map(|x| ...unwrap())
2784
- if let ExprKind :: Closure ( _, _, map_body_id, ..) = map_arg. kind;
2785
- let map_body = cx. tcx. hir( ) . body( map_body_id) ;
2786
- if let [ map_param] = map_body. params;
2787
- if let PatKind :: Binding ( _, map_param_id, map_param_ident, None ) = map_param. pat. kind;
2788
- // closure ends with expect() or unwrap()
2789
- if let ExprKind :: MethodCall ( seg, _, [ map_arg, ..] , _) = map_body. value. kind;
2790
- if matches!( seg. ident. name, sym:: expect | sym:: unwrap | sym:: unwrap_or) ;
2791
-
2792
- let eq_fallback = |a: & Expr <' _>, b: & Expr <' _>| {
2793
- // in `filter(|x| ..)`, replace `*x` with `x`
2794
- let a_path = if_chain! {
2795
- if !is_filter_param_ref;
2796
- if let ExprKind :: Unary ( UnOp :: Deref , expr_path) = a. kind;
2797
- then { expr_path } else { a }
2798
- } ;
2799
- // let the filter closure arg and the map closure arg be equal
2800
- if_chain! {
2801
- if path_to_local_id( a_path, filter_param_id) ;
2802
- if path_to_local_id( b, map_param_id) ;
2803
- if TyS :: same_type( cx. typeck_results( ) . expr_ty_adjusted( a) , cx. typeck_results( ) . expr_ty_adjusted( b) ) ;
2804
- then {
2805
- return true ;
2806
- }
2807
- }
2808
- false
2809
- } ;
2810
- if SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg) ;
2811
- then {
2812
- let span = filter_span. to( map_span) ;
2813
- let ( filter_name, lint) = if is_find {
2814
- ( "find" , MANUAL_FIND_MAP )
2815
- } else {
2816
- ( "filter" , MANUAL_FILTER_MAP )
2817
- } ;
2818
- let msg = format!( "`{}(..).map(..)` can be simplified as `{0}_map(..)`" , filter_name) ;
2819
- let to_opt = if is_result { ".ok()" } else { "" } ;
2820
- let sugg = format!( "{}_map(|{}| {}{})" , filter_name, map_param_ident,
2821
- snippet( cx, map_arg. span, ".." ) , to_opt) ;
2822
- span_lint_and_sugg( cx, lint, span, & msg, "try" , sugg, Applicability :: MachineApplicable ) ;
2823
- }
2824
- }
2825
- }
2826
-
2827
2754
const FILTER_MAP_NEXT_MSRV : RustcVersion = RustcVersion :: new ( 1 , 30 , 0 ) ;
2828
2755
2829
2756
/// lint use of `filter_map().next()` for `Iterators`
0 commit comments