@@ -2,9 +2,13 @@ use super::FILTER_MAP_BOOL_THEN;
2
2
use clippy_utils:: diagnostics:: span_lint_and_then;
3
3
use clippy_utils:: source:: SpanRangeExt ;
4
4
use clippy_utils:: ty:: is_copy;
5
- use clippy_utils:: { contains_return, is_from_proc_macro, is_trait_method, peel_blocks} ;
5
+ use clippy_utils:: {
6
+ CaptureKind , can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks,
7
+ } ;
8
+ use rustc_ast:: Mutability ;
9
+ use rustc_data_structures:: fx:: FxHashSet ;
6
10
use rustc_errors:: Applicability ;
7
- use rustc_hir:: { Expr , ExprKind } ;
11
+ use rustc_hir:: { Expr , ExprKind , HirId , Param , Pat } ;
8
12
use rustc_lint:: { LateContext , LintContext } ;
9
13
use rustc_middle:: ty:: Binder ;
10
14
use rustc_middle:: ty:: adjustment:: Adjust ;
@@ -50,9 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
50
54
call_span,
51
55
"usage of `bool::then` in `filter_map`" ,
52
56
|diag| {
53
- if contains_return ( recv) {
54
- diag. help ( "consider using `filter` then `map` instead" ) ;
55
- } else {
57
+ if can_filter_and_then_move_to_closure ( cx, & param, recv, then_body) {
56
58
diag. span_suggestion (
57
59
call_span,
58
60
"use `filter` then `map` instead" ,
@@ -62,8 +64,53 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
62
64
) ,
63
65
Applicability :: MachineApplicable ,
64
66
) ;
67
+ } else {
68
+ diag. help ( "consider using `filter` then `map` instead" ) ;
65
69
}
66
70
} ,
67
71
) ;
68
72
}
69
73
}
74
+
75
+ /// Returns a set of all bindings found in the given pattern.
76
+ fn find_bindings_from_pat ( pat : & Pat < ' _ > ) -> FxHashSet < HirId > {
77
+ let mut bindings = FxHashSet :: default ( ) ;
78
+ pat. walk ( |p| {
79
+ if let rustc_hir:: PatKind :: Binding ( _, hir_id, _, _) = p. kind {
80
+ bindings. insert ( hir_id) ;
81
+ }
82
+ true
83
+ } ) ;
84
+ bindings
85
+ }
86
+
87
+ /// Returns true if we can take a closure parameter and have it in both the `filter` function and
88
+ /// the`map` function. This is not the case if:
89
+ ///
90
+ /// - The `filter` would contain an early return,
91
+ /// - `filter` and `then` contain captures, and any of those are &mut
92
+ fn can_filter_and_then_move_to_closure < ' tcx > (
93
+ cx : & LateContext < ' tcx > ,
94
+ param : & Param < ' tcx > ,
95
+ filter : & ' tcx Expr < ' tcx > ,
96
+ then : & ' tcx Expr < ' tcx > ,
97
+ ) -> bool {
98
+ if contains_return ( filter) {
99
+ return false ;
100
+ }
101
+
102
+ let Some ( filter_captures) = can_move_expr_to_closure ( cx, filter) else {
103
+ return true ;
104
+ } ;
105
+ let Some ( then_captures) = can_move_expr_to_closure ( cx, then) else {
106
+ return true ;
107
+ } ;
108
+
109
+ let param_bindings = find_bindings_from_pat ( param. pat ) ;
110
+ filter_captures. iter ( ) . all ( |( hir_id, filter_cap) | {
111
+ param_bindings. contains ( hir_id)
112
+ || !then_captures
113
+ . get ( hir_id)
114
+ . is_some_and ( |then_cap| matches ! ( * filter_cap | * then_cap, CaptureKind :: Ref ( Mutability :: Mut ) ) )
115
+ } )
116
+ }
0 commit comments