@@ -1383,6 +1383,27 @@ declare_clippy_lint! {
1383
1383
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
1384
1384
}
1385
1385
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
+
1386
1407
declare_lint_pass ! ( Methods => [
1387
1408
UNWRAP_USED ,
1388
1409
EXPECT_USED ,
@@ -1433,6 +1454,7 @@ declare_lint_pass!(Methods => [
1433
1454
FILETYPE_IS_FILE ,
1434
1455
OPTION_AS_REF_DEREF ,
1435
1456
UNNECESSARY_LAZY_EVALUATIONS ,
1457
+ MAP_COLLECT_RESULT_UNIT ,
1436
1458
] ) ;
1437
1459
1438
1460
impl < ' tcx > LateLintPass < ' tcx > for Methods {
@@ -1515,6 +1537,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
1515
1537
[ "unwrap_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "unwrap_or" ) ,
1516
1538
[ "get_or_insert_with" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "get_or_insert" ) ,
1517
1539
[ "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 ] ) ,
1518
1541
_ => { } ,
1519
1542
}
1520
1543
@@ -3501,6 +3524,42 @@ fn lint_option_as_ref_deref<'tcx>(
3501
3524
}
3502
3525
}
3503
3526
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
+
3504
3563
/// Given a `Result<T, E>` type, return its error type (`E`).
3505
3564
fn get_error_type < ' a > ( cx : & LateContext < ' _ > , ty : Ty < ' a > ) -> Option < Ty < ' a > > {
3506
3565
match ty. kind ( ) {
0 commit comments