@@ -14,7 +14,7 @@ use rustc_span::symbol::{sym, Ident, Symbol};
14
14
use rustc_span:: Span ;
15
15
use std:: borrow:: Cow ;
16
16
17
- use super :: { MANUAL_FILTER_MAP , MANUAL_FIND_MAP , OPTION_FILTER_MAP } ;
17
+ use super :: { MANUAL_FILTER_MAP , MANUAL_FIND_MAP , OPTION_FILTER_MAP , RESULT_FILTER_MAP } ;
18
18
19
19
fn is_method ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > , method_name : Symbol ) -> bool {
20
20
match & expr. kind {
@@ -46,6 +46,9 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) ->
46
46
fn is_option_filter_map ( cx : & LateContext < ' _ > , filter_arg : & hir:: Expr < ' _ > , map_arg : & hir:: Expr < ' _ > ) -> bool {
47
47
is_method ( cx, map_arg, sym:: unwrap) && is_method ( cx, filter_arg, sym ! ( is_some) )
48
48
}
49
+ fn is_ok_filter_map ( cx : & LateContext < ' _ > , filter_arg : & hir:: Expr < ' _ > , map_arg : & hir:: Expr < ' _ > ) -> bool {
50
+ is_method ( cx, map_arg, sym:: unwrap) && is_method ( cx, filter_arg, sym ! ( is_ok) )
51
+ }
49
52
50
53
#[ derive( Debug , Copy , Clone ) ]
51
54
enum OffendingFilterExpr < ' tcx > {
@@ -273,6 +276,18 @@ fn is_filter_some_map_unwrap(
273
276
( iterator || option) && is_option_filter_map ( cx, filter_arg, map_arg)
274
277
}
275
278
279
+ /// is `filter(|x| x.is_ok()).map(|x| x.unwrap())`
280
+ fn is_filter_ok_map_unwrap (
281
+ cx : & LateContext < ' _ > ,
282
+ expr : & hir:: Expr < ' _ > ,
283
+ filter_arg : & hir:: Expr < ' _ > ,
284
+ map_arg : & hir:: Expr < ' _ > ,
285
+ ) -> bool {
286
+ // result has no filter, so we only check for iterators
287
+ let iterator = is_trait_method ( cx, expr, sym:: Iterator ) ;
288
+ iterator && is_ok_filter_map ( cx, filter_arg, map_arg)
289
+ }
290
+
276
291
/// lint use of `filter().map()` or `find().map()` for `Iterators`
277
292
#[ allow( clippy:: too_many_arguments) ]
278
293
pub ( super ) fn check (
@@ -300,30 +315,21 @@ pub(super) fn check(
300
315
return ;
301
316
}
302
317
303
- if is_trait_method ( cx, map_recv, sym:: Iterator )
304
-
305
- // filter(|x| ...is_some())...
306
- && let ExprKind :: Closure ( & Closure { body : filter_body_id, .. } ) = filter_arg. kind
307
- && let filter_body = cx. tcx . hir ( ) . body ( filter_body_id)
308
- && let [ filter_param] = filter_body. params
309
- // optional ref pattern: `filter(|&x| ..)`
310
- && let ( filter_pat, is_filter_param_ref) = if let PatKind :: Ref ( ref_pat, _) = filter_param. pat . kind {
311
- ( ref_pat, true )
312
- } else {
313
- ( filter_param. pat , false )
314
- }
315
-
316
- && let PatKind :: Binding ( _, filter_param_id, _, None ) = filter_pat. kind
317
- && let Some ( mut offending_expr) = OffendingFilterExpr :: hir ( cx, filter_body. value , filter_param_id)
318
+ if is_filter_ok_map_unwrap ( cx, expr, filter_arg, map_arg) {
319
+ span_lint_and_sugg (
320
+ cx,
321
+ RESULT_FILTER_MAP ,
322
+ filter_span. with_hi ( expr. span . hi ( ) ) ,
323
+ "`filter` for `Ok` followed by `unwrap`" ,
324
+ "consider using `flatten` instead" ,
325
+ reindent_multiline ( Cow :: Borrowed ( "flatten()" ) , true , indent_of ( cx, map_span) ) . into_owned ( ) ,
326
+ Applicability :: MachineApplicable ,
327
+ ) ;
318
328
319
- && let ExprKind :: Closure ( & Closure { body : map_body_id, .. } ) = map_arg. kind
320
- && let map_body = cx. tcx . hir ( ) . body ( map_body_id)
321
- && let [ map_param] = map_body. params
322
- && let PatKind :: Binding ( _, map_param_id, map_param_ident, None ) = map_param. pat . kind
329
+ return ;
330
+ }
323
331
324
- && let Some ( check_result) =
325
- offending_expr. check_map_call ( cx, map_body, map_param_id, filter_param_id, is_filter_param_ref)
326
- {
332
+ if let Some ( ( map_param_ident, check_result) ) = is_find_or_filter ( cx, map_recv, filter_arg, map_arg) {
327
333
let span = filter_span. with_hi ( expr. span . hi ( ) ) ;
328
334
let ( filter_name, lint) = if is_find {
329
335
( "find" , MANUAL_FIND_MAP )
@@ -395,6 +401,40 @@ pub(super) fn check(
395
401
}
396
402
}
397
403
404
+ fn is_find_or_filter < ' a > (
405
+ cx : & LateContext < ' a > ,
406
+ map_recv : & hir:: Expr < ' _ > ,
407
+ filter_arg : & hir:: Expr < ' _ > ,
408
+ map_arg : & hir:: Expr < ' _ > ,
409
+ ) -> Option < ( Ident , CheckResult < ' a > ) > {
410
+ if is_trait_method ( cx, map_recv, sym:: Iterator )
411
+ // filter(|x| ...is_some())...
412
+ && let ExprKind :: Closure ( & Closure { body : filter_body_id, .. } ) = filter_arg. kind
413
+ && let filter_body = cx. tcx . hir ( ) . body ( filter_body_id)
414
+ && let [ filter_param] = filter_body. params
415
+ // optional ref pattern: `filter(|&x| ..)`
416
+ && let ( filter_pat, is_filter_param_ref) = if let PatKind :: Ref ( ref_pat, _) = filter_param. pat . kind {
417
+ ( ref_pat, true )
418
+ } else {
419
+ ( filter_param. pat , false )
420
+ }
421
+
422
+ && let PatKind :: Binding ( _, filter_param_id, _, None ) = filter_pat. kind
423
+ && let Some ( mut offending_expr) = OffendingFilterExpr :: hir ( cx, filter_body. value , filter_param_id)
424
+
425
+ && let ExprKind :: Closure ( & Closure { body : map_body_id, .. } ) = map_arg. kind
426
+ && let map_body = cx. tcx . hir ( ) . body ( map_body_id)
427
+ && let [ map_param] = map_body. params
428
+ && let PatKind :: Binding ( _, map_param_id, map_param_ident, None ) = map_param. pat . kind
429
+
430
+ && let Some ( check_result) =
431
+ offending_expr. check_map_call ( cx, map_body, map_param_id, filter_param_id, is_filter_param_ref)
432
+ {
433
+ return Some ( ( map_param_ident, check_result) ) ;
434
+ }
435
+ None
436
+ }
437
+
398
438
fn acceptable_methods ( method : & PathSegment < ' _ > ) -> bool {
399
439
let methods: [ Symbol ; 8 ] = [
400
440
sym:: clone,
0 commit comments