@@ -1383,6 +1383,31 @@ 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+ /// let iter = vec![0; 2].iter();
1397+ ///
1398+ /// iter.map(|t| Err(t)).collect::<Result<(), _>>();
1399+ /// ```
1400+ /// Use instead:
1401+ /// ```rust
1402+ /// let iter = vec![0; 2].iter();
1403+ ///
1404+ /// iter.try_for_each(|t| Err(t));
1405+ /// ```
1406+ pub MAP_COLLECT_RESULT_UNIT ,
1407+ style,
1408+ "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
1409+ }
1410+
13861411declare_lint_pass ! ( Methods => [
13871412 UNWRAP_USED ,
13881413 EXPECT_USED ,
@@ -1433,6 +1458,7 @@ declare_lint_pass!(Methods => [
14331458 FILETYPE_IS_FILE ,
14341459 OPTION_AS_REF_DEREF ,
14351460 UNNECESSARY_LAZY_EVALUATIONS ,
1461+ MAP_COLLECT_RESULT_UNIT ,
14361462] ) ;
14371463
14381464impl < ' tcx > LateLintPass < ' tcx > for Methods {
@@ -1515,6 +1541,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
15151541 [ "unwrap_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "unwrap_or" ) ,
15161542 [ "get_or_insert_with" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "get_or_insert" ) ,
15171543 [ "ok_or_else" , ..] => unnecessary_lazy_eval:: lint ( cx, expr, arg_lists[ 0 ] , "ok_or" ) ,
1544+ [ "collect" , "map" ] => lint_map_collect ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
15181545 _ => { } ,
15191546 }
15201547
@@ -3501,6 +3528,42 @@ fn lint_option_as_ref_deref<'tcx>(
35013528 }
35023529}
35033530
3531+ fn lint_map_collect (
3532+ cx : & LateContext < ' _ > ,
3533+ expr : & hir:: Expr < ' _ > ,
3534+ map_args : & [ hir:: Expr < ' _ > ] ,
3535+ collect_args : & [ hir:: Expr < ' _ > ] ,
3536+ ) {
3537+ if_chain ! {
3538+ // called on Iterator
3539+ if let [ map_expr] = collect_args;
3540+ if match_trait_method( cx, map_expr, & paths:: ITERATOR ) ;
3541+ // return of collect `Result<(),_>`
3542+ let collect_ret_ty = cx. typeck_results( ) . expr_ty( expr) ;
3543+ if is_type_diagnostic_item( cx, collect_ret_ty, sym!( result_type) ) ;
3544+ if let ty:: Adt ( _, substs) = collect_ret_ty. kind( ) ;
3545+ if let Some ( result_t) = substs. types( ) . next( ) ;
3546+ if result_t. is_unit( ) ;
3547+ // get parts for snippet
3548+ if let [ iter, map_fn] = map_args;
3549+ then {
3550+ span_lint_and_sugg(
3551+ cx,
3552+ MAP_COLLECT_RESULT_UNIT ,
3553+ expr. span,
3554+ "`.map().collect()` can be replaced with `.try_for_each()`" ,
3555+ "try this" ,
3556+ format!(
3557+ "{}.try_for_each({})" ,
3558+ snippet( cx, iter. span, ".." ) ,
3559+ snippet( cx, map_fn. span, ".." )
3560+ ) ,
3561+ Applicability :: MachineApplicable ,
3562+ ) ;
3563+ }
3564+ }
3565+ }
3566+
35043567/// Given a `Result<T, E>` type, return its error type (`E`).
35053568fn get_error_type < ' a > ( cx : & LateContext < ' _ > , ty : Ty < ' a > ) -> Option < Ty < ' a > > {
35063569 match ty. kind ( ) {
0 commit comments