@@ -8,6 +8,7 @@ use rustc_hir as hir;
8
8
use rustc_hir:: def:: Res ;
9
9
use rustc_hir:: { Expr , ExprKind , PatKind , PathSegment , QPath , UnOp } ;
10
10
use rustc_lint:: LateContext ;
11
+ use rustc_middle:: ty:: adjustment:: Adjust ;
11
12
use rustc_span:: source_map:: Span ;
12
13
use rustc_span:: symbol:: { sym, Symbol } ;
13
14
use std:: borrow:: Cow ;
@@ -49,35 +50,18 @@ fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>
49
50
is_method ( cx, map_arg, sym:: unwrap) && is_method ( cx, filter_arg, sym ! ( is_some) )
50
51
}
51
52
52
- /// lint use of `filter() .map()` for `Iterators `
53
- fn lint_filter_some_map_unwrap (
53
+ /// is `filter(|x| x.is_some()) .map(|x| x.unwrap()) `
54
+ fn is_filter_some_map_unwrap (
54
55
cx : & LateContext < ' _ > ,
55
56
expr : & hir:: Expr < ' _ > ,
56
57
filter_recv : & hir:: Expr < ' _ > ,
57
58
filter_arg : & hir:: Expr < ' _ > ,
58
59
map_arg : & hir:: Expr < ' _ > ,
59
- target_span : Span ,
60
- methods_span : Span ,
61
- ) {
60
+ ) -> bool {
62
61
let iterator = is_trait_method ( cx, expr, sym:: Iterator ) ;
63
62
let option = is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( filter_recv) , sym:: Option ) ;
64
- if ( iterator || option) && is_option_filter_map ( cx, filter_arg, map_arg) {
65
- let msg = "`filter` for `Some` followed by `unwrap`" ;
66
- let help = "consider using `flatten` instead" ;
67
- let sugg = format ! (
68
- "{}" ,
69
- reindent_multiline( Cow :: Borrowed ( "flatten()" ) , true , indent_of( cx, target_span) , )
70
- ) ;
71
- span_lint_and_sugg (
72
- cx,
73
- OPTION_FILTER_MAP ,
74
- methods_span,
75
- msg,
76
- help,
77
- sugg,
78
- Applicability :: MachineApplicable ,
79
- ) ;
80
- }
63
+
64
+ ( iterator || option) && is_option_filter_map ( cx, filter_arg, map_arg)
81
65
}
82
66
83
67
/// lint use of `filter().map()` or `find().map()` for `Iterators`
@@ -93,15 +77,20 @@ pub(super) fn check<'tcx>(
93
77
map_span : Span ,
94
78
is_find : bool ,
95
79
) {
96
- lint_filter_some_map_unwrap (
97
- cx,
98
- expr,
99
- filter_recv,
100
- filter_arg,
101
- map_arg,
102
- map_span,
103
- filter_span. with_hi ( expr. span . hi ( ) ) ,
104
- ) ;
80
+ if is_filter_some_map_unwrap ( cx, expr, filter_recv, filter_arg, map_arg) {
81
+ span_lint_and_sugg (
82
+ cx,
83
+ OPTION_FILTER_MAP ,
84
+ filter_span. with_hi ( expr. span . hi ( ) ) ,
85
+ "`filter` for `Some` followed by `unwrap`" ,
86
+ "consider using `flatten` instead" ,
87
+ reindent_multiline ( Cow :: Borrowed ( "flatten()" ) , true , indent_of ( cx, map_span) ) . into_owned ( ) ,
88
+ Applicability :: MachineApplicable ,
89
+ ) ;
90
+
91
+ return ;
92
+ }
93
+
105
94
if_chain ! {
106
95
if is_trait_method( cx, map_recv, sym:: Iterator ) ;
107
96
@@ -118,7 +107,7 @@ pub(super) fn check<'tcx>(
118
107
// closure ends with is_some() or is_ok()
119
108
if let PatKind :: Binding ( _, filter_param_id, _, None ) = filter_pat. kind;
120
109
if let ExprKind :: MethodCall ( path, [ filter_arg] , _) = filter_body. value. kind;
121
- if let Some ( opt_ty) = cx. typeck_results( ) . expr_ty( filter_arg) . ty_adt_def( ) ;
110
+ if let Some ( opt_ty) = cx. typeck_results( ) . expr_ty( filter_arg) . peel_refs ( ) . ty_adt_def( ) ;
122
111
if let Some ( is_result) = if cx. tcx. is_diagnostic_item( sym:: Option , opt_ty. did( ) ) {
123
112
Some ( false )
124
113
} else if cx. tcx. is_diagnostic_item( sym:: Result , opt_ty. did( ) ) {
@@ -137,6 +126,19 @@ pub(super) fn check<'tcx>(
137
126
if let ExprKind :: MethodCall ( seg, [ map_arg, ..] , _) = map_body. value. kind;
138
127
if matches!( seg. ident. name, sym:: expect | sym:: unwrap | sym:: unwrap_or) ;
139
128
129
+ // .filter(..).map(|y| f(y).copied().unwrap())
130
+ // ~~~~
131
+ let map_arg_peeled = match map_arg. kind {
132
+ ExprKind :: MethodCall ( method, [ original_arg] , _) if acceptable_methods( method) => {
133
+ original_arg
134
+ } ,
135
+ _ => map_arg,
136
+ } ;
137
+
138
+ // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
139
+ let simple_equal = path_to_local_id( filter_arg, filter_param_id)
140
+ && path_to_local_id( map_arg_peeled, map_param_id) ;
141
+
140
142
let eq_fallback = |a: & Expr <' _>, b: & Expr <' _>| {
141
143
// in `filter(|x| ..)`, replace `*x` with `x`
142
144
let a_path = if_chain! {
@@ -145,36 +147,35 @@ pub(super) fn check<'tcx>(
145
147
then { expr_path } else { a }
146
148
} ;
147
149
// let the filter closure arg and the map closure arg be equal
148
- if_chain! {
149
- if path_to_local_id( a_path, filter_param_id) ;
150
- if path_to_local_id( b, map_param_id) ;
151
- if cx. typeck_results( ) . expr_ty_adjusted( a) == cx. typeck_results( ) . expr_ty_adjusted( b) ;
152
- then {
153
- return true ;
154
- }
155
- }
156
- false
157
- } ;
158
-
159
- if match map_arg. kind {
160
- ExprKind :: MethodCall ( method, [ original_arg] , _) => {
161
- acceptable_methods( method)
162
- && SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, original_arg)
163
- } ,
164
- _ => SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg)
150
+ path_to_local_id( a_path, filter_param_id)
151
+ && path_to_local_id( b, map_param_id)
152
+ && cx. typeck_results( ) . expr_ty_adjusted( a) == cx. typeck_results( ) . expr_ty_adjusted( b)
165
153
} ;
166
154
155
+ if simple_equal || SpanlessEq :: new( cx) . expr_fallback( eq_fallback) . eq_expr( filter_arg, map_arg_peeled) ;
167
156
then {
168
157
let span = filter_span. with_hi( expr. span. hi( ) ) ;
169
158
let ( filter_name, lint) = if is_find {
170
159
( "find" , MANUAL_FIND_MAP )
171
160
} else {
172
161
( "filter" , MANUAL_FILTER_MAP )
173
162
} ;
174
- let msg = format!( "`{}(..).map(..)` can be simplified as `{0}_map(..)`" , filter_name) ;
175
- let to_opt = if is_result { ".ok()" } else { "" } ;
176
- let sugg = format!( "{}_map(|{}| {}{})" , filter_name, map_param_ident,
177
- snippet( cx, map_arg. span, ".." ) , to_opt) ;
163
+ let msg = format!( "`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`" ) ;
164
+ let ( to_opt, deref) = if is_result {
165
+ ( ".ok()" , String :: new( ) )
166
+ } else {
167
+ let derefs = cx. typeck_results( )
168
+ . expr_adjustments( map_arg)
169
+ . iter( )
170
+ . filter( |adj| matches!( adj. kind, Adjust :: Deref ( _) ) )
171
+ . count( ) ;
172
+
173
+ ( "" , "*" . repeat( derefs) )
174
+ } ;
175
+ let sugg = format!(
176
+ "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})" ,
177
+ snippet( cx, map_arg. span, ".." ) ,
178
+ ) ;
178
179
span_lint_and_sugg( cx, lint, span, & msg, "try" , sugg, Applicability :: MachineApplicable ) ;
179
180
}
180
181
}
0 commit comments