@@ -342,6 +342,28 @@ declare_clippy_lint! {
342342 "using combination of `filter_map` and `next` which can usually be written as a single method call"
343343}
344344
345+ declare_clippy_lint ! {
346+ /// **What it does:** Checks for usage of `flat_map(|x| x)`.
347+ ///
348+ /// **Why is this bad?** Readability, this can be written more concisely by using `flatten`.
349+ ///
350+ /// **Known problems:** None
351+ ///
352+ /// **Example:**
353+ /// ```rust
354+ /// # let iter = vec![vec![0]].into_iter();
355+ /// iter.flat_map(|x| x);
356+ /// ```
357+ /// Can be written as
358+ /// ```rust
359+ /// # let iter = vec![vec![0]].into_iter();
360+ /// iter.flatten();
361+ /// ```
362+ pub FLAT_MAP_IDENTITY ,
363+ complexity,
364+ "call to `flat_map` where `flatten` is sufficient"
365+ }
366+
345367declare_clippy_lint ! {
346368 /// **What it does:** Checks for usage of `_.find(_).map(_)`.
347369 ///
@@ -892,6 +914,7 @@ declare_lint_pass!(Methods => [
892914 FILTER_NEXT ,
893915 FILTER_MAP ,
894916 FILTER_MAP_NEXT ,
917+ FLAT_MAP_IDENTITY ,
895918 FIND_MAP ,
896919 MAP_FLATTEN ,
897920 ITER_NTH ,
@@ -932,6 +955,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
932955 [ "map" , "find" ] => lint_find_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
933956 [ "flat_map" , "filter" ] => lint_filter_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
934957 [ "flat_map" , "filter_map" ] => lint_filter_map_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
958+ [ "flat_map" , ..] => lint_flat_map_identity ( cx, expr, arg_lists[ 0 ] ) ,
935959 [ "flatten" , "map" ] => lint_map_flatten ( cx, expr, arg_lists[ 1 ] ) ,
936960 [ "is_some" , "find" ] => lint_search_is_some ( cx, expr, "find" , arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
937961 [ "is_some" , "position" ] => lint_search_is_some ( cx, expr, "position" , arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
@@ -2143,6 +2167,56 @@ fn lint_filter_map_flat_map<'a, 'tcx>(
21432167 }
21442168}
21452169
2170+ /// lint use of `flat_map` for `Iterators` where `flatten` would be sufficient
2171+ fn lint_flat_map_identity < ' a , ' tcx > (
2172+ cx : & LateContext < ' a , ' tcx > ,
2173+ expr : & ' tcx hir:: Expr ,
2174+ flat_map_args : & ' tcx [ hir:: Expr ] ,
2175+ ) {
2176+ if match_trait_method ( cx, expr, & paths:: ITERATOR ) {
2177+ let arg_node = & flat_map_args[ 1 ] . node ;
2178+
2179+ let apply_lint = |message : & str | {
2180+ if let hir:: ExprKind :: MethodCall ( _, span, _) = & expr. node {
2181+ span_lint_and_sugg (
2182+ cx,
2183+ FLAT_MAP_IDENTITY ,
2184+ span. with_hi ( expr. span . hi ( ) ) ,
2185+ message,
2186+ "try" ,
2187+ "flatten()" . to_string ( ) ,
2188+ Applicability :: MachineApplicable ,
2189+ ) ;
2190+ }
2191+ } ;
2192+
2193+ if_chain ! {
2194+ if let hir:: ExprKind :: Closure ( _, _, body_id, _, _) = arg_node;
2195+ let body = cx. tcx. hir( ) . body( * body_id) ;
2196+
2197+ if let hir:: PatKind :: Binding ( _, _, binding_ident, _) = body. arguments[ 0 ] . pat. node;
2198+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, ref path) ) = body. value. node;
2199+
2200+ if path. segments. len( ) == 1 ;
2201+ if path. segments[ 0 ] . ident. as_str( ) == binding_ident. as_str( ) ;
2202+
2203+ then {
2204+ apply_lint( "called `flat_map(|x| x)` on an `Iterator`" ) ;
2205+ }
2206+ }
2207+
2208+ if_chain ! {
2209+ if let hir:: ExprKind :: Path ( ref qpath) = arg_node;
2210+
2211+ if match_qpath( qpath, & paths:: STD_CONVERT_IDENTITY ) ;
2212+
2213+ then {
2214+ apply_lint( "called `flat_map(std::convert::identity)` on an `Iterator`" ) ;
2215+ }
2216+ }
2217+ }
2218+ }
2219+
21462220/// lint searching an Iterator followed by `is_some()`
21472221fn lint_search_is_some < ' a , ' tcx > (
21482222 cx : & LateContext < ' a , ' tcx > ,
0 commit comments