@@ -1383,6 +1383,27 @@ declare_clippy_lint! {
13831383 "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
13841384}
13851385
1386+ declare_clippy_lint ! {
1387+ /// **What it does:** Checks for usage of `_.map(_).collect::<Result<(),_>()`.
1388+ ///
1389+ /// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.
1390+ ///
1391+ /// **Known problems:** None
1392+ ///
1393+ /// **Example:**
1394+ ///
1395+ /// ```rust
1396+ /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
1397+ /// ```
1398+ /// Use instead:
1399+ /// ```rust
1400+ /// (0..3).try_for_each(|t| Err(t));
1401+ /// ```
1402+ pub MAP_COLLECT_RESULT_UNIT ,
1403+ style,
1404+ "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
1405+ }
1406+
13861407declare_lint_pass ! ( Methods => [
13871408 UNWRAP_USED ,
13881409 EXPECT_USED ,
@@ -1433,6 +1454,7 @@ declare_lint_pass!(Methods => [
14331454 FILETYPE_IS_FILE ,
14341455 OPTION_AS_REF_DEREF ,
14351456 UNNECESSARY_LAZY_EVALUATIONS ,
1457+ MAP_COLLECT_RESULT_UNIT ,
14361458] ) ;
14371459
14381460impl < ' tcx > LateLintPass < ' tcx > for Methods {
@@ -1515,6 +1537,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
15151537 [ "unwrap_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "unwrap_or" ) ,
15161538 [ "get_or_insert_with" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "get_or_insert" ) ,
15171539 [ "ok_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "ok_or" ) ,
1540+ [ "collect" , "map" ] => lint_map_collect ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
15181541 _ => { } ,
15191542 }
15201543
@@ -3501,6 +3524,42 @@ fn lint_option_as_ref_deref<'tcx>(
35013524 }
35023525}
35033526
3527+ fn lint_map_collect (
3528+ cx : & LateContext < ' _ > ,
3529+ expr : & hir:: Expr < ' _ > ,
3530+ map_args : & [ hir:: Expr < ' _ > ] ,
3531+ collect_args : & [ hir:: Expr < ' _ > ] ,
3532+ ) {
3533+ if_chain ! {
3534+ // called on Iterator
3535+ if let [ map_expr] = collect_args;
3536+ if match_trait_method( cx, map_expr, & paths:: ITERATOR ) ;
3537+ // return of collect `Result<(),_>`
3538+ let collect_ret_ty = cx. typeck_results( ) . expr_ty( expr) ;
3539+ if is_type_diagnostic_item( cx, collect_ret_ty, sym!( result_type) ) ;
3540+ if let ty:: Adt ( _, substs) = collect_ret_ty. kind( ) ;
3541+ if let Some ( result_t) = substs. types( ) . next( ) ;
3542+ if result_t. is_unit( ) ;
3543+ // get parts for snippet
3544+ if let [ iter, map_fn] = map_args;
3545+ then {
3546+ span_lint_and_sugg(
3547+ cx,
3548+ MAP_COLLECT_RESULT_UNIT ,
3549+ expr. span,
3550+ "`.map().collect()` can be replaced with `.try_for_each()`" ,
3551+ "try this" ,
3552+ format!(
3553+ "{}.try_for_each({})" ,
3554+ snippet( cx, iter. span, ".." ) ,
3555+ snippet( cx, map_fn. span, ".." )
3556+ ) ,
3557+ Applicability :: MachineApplicable ,
3558+ ) ;
3559+ }
3560+ }
3561+ }
3562+
35043563/// Given a `Result<T, E>` type, return its error type (`E`).
35053564fn get_error_type < ' a > ( cx : & LateContext < ' _ > , ty : Ty < ' a > ) -> Option < Ty < ' a > > {
35063565 match ty. kind ( ) {
0 commit comments