@@ -312,6 +312,26 @@ declare_clippy_lint! {
312
312
"using combination of `filter_map` and `next` which can usually be written as a single method call"
313
313
}
314
314
315
+ declare_clippy_lint ! {
316
+ /// **What it does:** Checks for usage of `flat_map(|x| x)`.
317
+ ///
318
+ /// **Why is this bad?** Readability, this can be written more concisely by using `flatten`.
319
+ ///
320
+ /// **Known problems:** None
321
+ ///
322
+ /// **Example:**
323
+ /// ```rust
324
+ /// iter.flat_map(|x| x)
325
+ /// ```
326
+ /// Can be written as
327
+ /// ```rust
328
+ /// iter.flatten()
329
+ /// ```
330
+ pub FLAT_MAP ,
331
+ pedantic,
332
+ "call to `flat_map` where `flatten` is sufficient"
333
+ }
334
+
315
335
declare_clippy_lint ! {
316
336
/// **What it does:** Checks for usage of `_.find(_).map(_)`.
317
337
///
@@ -844,6 +864,7 @@ declare_lint_pass!(Methods => [
844
864
FILTER_NEXT ,
845
865
FILTER_MAP ,
846
866
FILTER_MAP_NEXT ,
867
+ FLAT_MAP ,
847
868
FIND_MAP ,
848
869
MAP_FLATTEN ,
849
870
ITER_NTH ,
@@ -884,6 +905,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
884
905
[ "map" , "find" ] => lint_find_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
885
906
[ "flat_map" , "filter" ] => lint_filter_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
886
907
[ "flat_map" , "filter_map" ] => lint_filter_map_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
908
+ [ "flat_map" , ..] => lint_flat_map ( cx, expr, arg_lists[ 0 ] ) ,
887
909
[ "flatten" , "map" ] => lint_map_flatten ( cx, expr, arg_lists[ 1 ] ) ,
888
910
[ "is_some" , "find" ] => lint_search_is_some ( cx, expr, "find" , arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
889
911
[ "is_some" , "position" ] => lint_search_is_some ( cx, expr, "position" , arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
@@ -2092,6 +2114,30 @@ fn lint_filter_map_flat_map<'a, 'tcx>(
2092
2114
}
2093
2115
}
2094
2116
2117
+ /// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient
2118
+ fn lint_flat_map < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr , flat_map_args : & ' tcx [ hir:: Expr ] ) {
2119
+ if_chain ! {
2120
+ if match_trait_method( cx, expr, & paths:: ITERATOR ) ;
2121
+
2122
+ if flat_map_args. len( ) == 2 ;
2123
+ if let hir:: ExprKind :: Closure ( _, _, body_id, _, _) = flat_map_args[ 1 ] . node;
2124
+ let body = cx. tcx. hir( ) . body( body_id) ;
2125
+
2126
+ if body. arguments. len( ) == 1 ;
2127
+ if let hir:: PatKind :: Binding ( _, _, binding_ident, _) = body. arguments[ 0 ] . pat. node;
2128
+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, ref path) ) = body. value. node;
2129
+
2130
+ if path. segments. len( ) == 1 ;
2131
+ if path. segments[ 0 ] . ident. as_str( ) == binding_ident. as_str( ) ;
2132
+
2133
+ then {
2134
+ let msg = "called `flat_map(|x| x)` on an `Iterator`. \
2135
+ This can be simplified by calling `flatten().`";
2136
+ span_lint( cx, FLAT_MAP , expr. span, msg) ;
2137
+ }
2138
+ }
2139
+ }
2140
+
2095
2141
/// lint searching an Iterator followed by `is_some()`
2096
2142
fn lint_search_is_some < ' a , ' tcx > (
2097
2143
cx : & LateContext < ' a , ' tcx > ,
0 commit comments