@@ -1349,6 +1349,27 @@ declare_clippy_lint! {
13491349 "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
13501350}
13511351
1352+ declare_clippy_lint ! {
1353+ /// **What it does:** Checks for usage of `_.map(_).collect::<Result<(),_>()`.
1354+ ///
1355+ /// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.
1356+ ///
1357+ /// **Known problems:** None
1358+ ///
1359+ /// **Example:**
1360+ ///
1361+ /// ```rust
1362+ /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
1363+ /// ```
1364+ /// Use instead:
1365+ /// ```rust
1366+ /// (0..3).try_for_each(|t| Err(t));
1367+ /// ```
1368+ pub MAP_COLLECT_RESULT_UNIT ,
1369+ style,
1370+ "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
1371+ }
1372+
13521373declare_lint_pass ! ( Methods => [
13531374 UNWRAP_USED ,
13541375 EXPECT_USED ,
@@ -1398,6 +1419,7 @@ declare_lint_pass!(Methods => [
13981419 FILETYPE_IS_FILE ,
13991420 OPTION_AS_REF_DEREF ,
14001421 UNNECESSARY_LAZY_EVALUATIONS ,
1422+ MAP_COLLECT_RESULT_UNIT ,
14011423] ) ;
14021424
14031425impl < ' tcx > LateLintPass < ' tcx > for Methods {
@@ -1479,6 +1501,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
14791501 [ "unwrap_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "unwrap_or" ) ,
14801502 [ "get_or_insert_with" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "get_or_insert" ) ,
14811503 [ "ok_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "ok_or" ) ,
1504+ [ "collect" , "map" ] => lint_map_collect ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
14821505 _ => { } ,
14831506 }
14841507
@@ -3445,6 +3468,42 @@ fn lint_option_as_ref_deref<'tcx>(
34453468 }
34463469}
34473470
3471+ fn lint_map_collect (
3472+ cx : & LateContext < ' _ > ,
3473+ expr : & hir:: Expr < ' _ > ,
3474+ map_args : & [ hir:: Expr < ' _ > ] ,
3475+ collect_args : & [ hir:: Expr < ' _ > ] ,
3476+ ) {
3477+ if_chain ! {
3478+ // called on Iterator
3479+ if let [ map_expr] = collect_args;
3480+ if match_trait_method( cx, map_expr, & paths:: ITERATOR ) ;
3481+ // return of collect `Result<(),_>`
3482+ let collect_ret_ty = cx. typeck_results( ) . expr_ty( expr) ;
3483+ if is_type_diagnostic_item( cx, collect_ret_ty, sym!( result_type) ) ;
3484+ if let ty:: Adt ( _, substs) = collect_ret_ty. kind( ) ;
3485+ if let Some ( result_t) = substs. types( ) . next( ) ;
3486+ if result_t. is_unit( ) ;
3487+ // get parts for snippet
3488+ if let [ iter, map_fn] = map_args;
3489+ then {
3490+ span_lint_and_sugg(
3491+ cx,
3492+ MAP_COLLECT_RESULT_UNIT ,
3493+ expr. span,
3494+ "`.map().collect()` can be replaced with `.try_for_each()`" ,
3495+ "try this" ,
3496+ format!(
3497+ "{}.try_for_each({})" ,
3498+ snippet( cx, iter. span, ".." ) ,
3499+ snippet( cx, map_fn. span, ".." )
3500+ ) ,
3501+ Applicability :: MachineApplicable ,
3502+ ) ;
3503+ }
3504+ }
3505+ }
3506+
34483507/// Given a `Result<T, E>` type, return its error type (`E`).
34493508fn get_error_type < ' a > ( cx : & LateContext < ' _ > , ty : Ty < ' a > ) -> Option < Ty < ' a > > {
34503509 match ty. kind ( ) {
0 commit comments