1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: paths:: BOOL_THEN ;
3
3
use 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} ;
5
6
use rustc_errors:: Applicability ;
6
7
use rustc_hir:: { Expr , ExprKind } ;
7
8
use rustc_lint:: { LateContext , LintContext } ;
8
9
use rustc_middle:: lint:: in_external_macro;
9
- use rustc_span:: Span ;
10
+ use rustc_span:: { sym , Span } ;
10
11
11
12
use super :: FILTER_MAP_BOOL_THEN ;
12
13
13
14
pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , arg : & Expr < ' _ > , call_span : Span ) {
14
15
if !in_external_macro ( cx. sess ( ) , expr. span )
16
+ && is_trait_method ( cx, expr, sym:: Iterator )
15
17
&& 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
18
26
&& let ExprKind :: Closure ( then_closure) = then_arg. kind
19
27
&& 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 )
21
29
&& match_def_path ( cx, def_id, & BOOL_THEN )
22
30
&& !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 )
26
32
&& let Some ( filter) = snippet_opt ( cx, recv. span )
27
33
&& let Some ( map) = snippet_opt ( cx, then_body. span )
28
34
{
@@ -32,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
32
38
call_span,
33
39
"usage of `bool::then` in `filter_map`" ,
34
40
"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})" ) ,
36
42
Applicability :: MachineApplicable ,
37
43
) ;
38
44
}
0 commit comments