@@ -14,7 +14,7 @@ use rustc_span::symbol::{sym, Ident, Symbol};
1414use rustc_span:: Span ;
1515use std:: borrow:: Cow ;
1616
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 } ;
1818
1919fn is_method ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > , method_name : Symbol ) -> bool {
2020 match & expr. kind {
@@ -46,6 +46,9 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) ->
4646fn is_option_filter_map ( cx : & LateContext < ' _ > , filter_arg : & hir:: Expr < ' _ > , map_arg : & hir:: Expr < ' _ > ) -> bool {
4747 is_method ( cx, map_arg, sym:: unwrap) && is_method ( cx, filter_arg, sym ! ( is_some) )
4848}
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+ }
4952
5053#[ derive( Debug , Copy , Clone ) ]
5154enum OffendingFilterExpr < ' tcx > {
@@ -273,6 +276,18 @@ fn is_filter_some_map_unwrap(
273276 ( iterator || option) && is_option_filter_map ( cx, filter_arg, map_arg)
274277}
275278
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+
276291/// lint use of `filter().map()` or `find().map()` for `Iterators`
277292#[ allow( clippy:: too_many_arguments) ]
278293pub ( super ) fn check (
@@ -300,30 +315,21 @@ pub(super) fn check(
300315 return ;
301316 }
302317
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+ ) ;
318328
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+ }
323331
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) {
327333 let span = filter_span. with_hi ( expr. span . hi ( ) ) ;
328334 let ( filter_name, lint) = if is_find {
329335 ( "find" , MANUAL_FIND_MAP )
@@ -395,6 +401,40 @@ pub(super) fn check(
395401 }
396402}
397403
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+
398438fn acceptable_methods ( method : & PathSegment < ' _ > ) -> bool {
399439 let methods: [ Symbol ; 8 ] = [
400440 sym:: clone,
0 commit comments