11use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: paths:: BOOL_THEN ;
33use clippy_utils:: source:: snippet_opt;
4- use clippy_utils:: { is_from_proc_macro, match_def_path, peel_blocks} ;
4+ use clippy_utils:: ty:: is_copy;
5+ use clippy_utils:: { is_from_proc_macro, is_trait_method, match_def_path, peel_blocks} ;
56use rustc_errors:: Applicability ;
67use rustc_hir:: { Expr , ExprKind } ;
78use rustc_lint:: { LateContext , LintContext } ;
89use rustc_middle:: lint:: in_external_macro;
9- use rustc_span:: Span ;
10+ use rustc_span:: { sym , Span } ;
1011
1112use super :: FILTER_MAP_BOOL_THEN ;
1213
1314pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , arg : & Expr < ' _ > , call_span : Span ) {
1415 if !in_external_macro ( cx. sess ( ) , expr. span )
16+ && is_trait_method ( cx, expr, sym:: Iterator )
1517 && let ExprKind :: Closure ( closure) = arg. kind
16- && let body = peel_blocks ( cx. tcx . hir ( ) . body ( closure. body ) . value )
17- && let ExprKind :: MethodCall ( _, recv, [ then_arg] , _) = body. kind
18+ && let body = cx. tcx . hir ( ) . body ( closure. body )
19+ && let value = peel_blocks ( body. value )
20+ // Indexing should be fine as `filter_map` always has 1 input, we unfortunately need both
21+ // `inputs` and `params` here as we need both the type and the span
22+ && let param_ty = closure. fn_decl . inputs [ 0 ]
23+ && let param = body. params [ 0 ]
24+ && is_copy ( cx, cx. typeck_results ( ) . node_type ( param_ty. hir_id ) . peel_refs ( ) )
25+ && let ExprKind :: MethodCall ( _, recv, [ then_arg] , _) = value. kind
1826 && let ExprKind :: Closure ( then_closure) = then_arg. kind
1927 && let then_body = peel_blocks ( cx. tcx . hir ( ) . body ( then_closure. body ) . value )
20- && let Some ( def_id) = cx. typeck_results ( ) . type_dependent_def_id ( body . hir_id )
28+ && let Some ( def_id) = cx. typeck_results ( ) . type_dependent_def_id ( value . hir_id )
2129 && match_def_path ( cx, def_id, & BOOL_THEN )
2230 && !is_from_proc_macro ( cx, expr)
23- && let Some ( decl_snippet) = closure. fn_arg_span . and_then ( |s| snippet_opt ( cx, s) )
24- // NOTE: This will include the `()` (parenthesis) around it. Maybe there's some utils method
25- // to remove them? `unused_parens` will already take care of this but it may be nice.
31+ && let Some ( param_snippet) = snippet_opt ( cx, param. span )
2632 && let Some ( filter) = snippet_opt ( cx, recv. span )
2733 && let Some ( map) = snippet_opt ( cx, then_body. span )
2834 {
@@ -32,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
3238 call_span,
3339 "usage of `bool::then` in `filter_map`" ,
3440 "use `filter` then `map` instead" ,
35- format ! ( "filter({decl_snippet} {filter}).map({decl_snippet} {map})" ) ,
41+ format ! ( "filter(|&{param_snippet}| {filter}).map(|{param_snippet}| {map})" ) ,
3642 Applicability :: MachineApplicable ,
3743 ) ;
3844 }
0 commit comments