@@ -494,13 +494,45 @@ declare_clippy_lint! {
494
494
"there is no reason to have a single element loop"
495
495
}
496
496
497
+ declare_clippy_lint ! {
498
+ /// **What it does:** Checks for iteration of `Option`s with
499
+ /// a single `if let Some()` expression inside.
500
+ ///
501
+ /// **Why is this bad?** It is verbose and can be simplified
502
+ /// by first calling the `flatten` method on the `Iterator`.
503
+ ///
504
+ /// **Known problems:** None.
505
+ ///
506
+ /// **Example:**
507
+ ///
508
+ /// ```rust
509
+ /// let x = vec![Some(1), Some(2), Some(3)];
510
+ /// for n in x {
511
+ /// if let Some(n) = n {
512
+ /// println!("{}", n);
513
+ /// }
514
+ /// }
515
+ /// ```
516
+ /// Use instead:
517
+ /// ```rust
518
+ /// let x = vec![Some(1), Some(2), Some(3)];
519
+ /// for n in x.iter().flatten() {
520
+ /// println!("{}", n);
521
+ /// }
522
+ /// ```
523
+ pub FOR_LOOPS_OVER_OPTIONS ,
524
+ complexity,
525
+ "for loops over `Option`s or `Result`s with a single expression can be simplified"
526
+ }
527
+
497
528
declare_lint_pass ! ( Loops => [
498
529
MANUAL_MEMCPY ,
499
530
NEEDLESS_RANGE_LOOP ,
500
531
EXPLICIT_ITER_LOOP ,
501
532
EXPLICIT_INTO_ITER_LOOP ,
502
533
ITER_NEXT_LOOP ,
503
534
FOR_LOOPS_OVER_FALLIBLES ,
535
+ FOR_LOOPS_OVER_OPTIONS ,
504
536
WHILE_LET_LOOP ,
505
537
NEEDLESS_COLLECT ,
506
538
EXPLICIT_COUNTER_LOOP ,
@@ -830,6 +862,7 @@ fn check_for_loop<'tcx>(
830
862
check_for_mut_range_bound ( cx, arg, body) ;
831
863
check_for_single_element_loop ( cx, pat, arg, body, expr) ;
832
864
detect_same_item_push ( cx, pat, arg, body, expr) ;
865
+ check_for_loop_over_options_or_results ( cx, pat, arg, body, expr) ;
833
866
}
834
867
835
868
// this function assumes the given expression is a `for` loop.
@@ -1953,6 +1986,37 @@ fn check_for_single_element_loop<'tcx>(
1953
1986
}
1954
1987
}
1955
1988
1989
+ /// Check if a for loop loops over `Option`s or `Result`s and contains only
1990
+ /// a `if let Some` or `if let Ok` expression.
1991
+ fn check_for_loop_over_options_or_results < ' tcx > (
1992
+ cx : & LateContext < ' tcx > ,
1993
+ pat : & ' tcx Pat < ' _ > ,
1994
+ arg : & ' tcx Expr < ' _ > ,
1995
+ body : & ' tcx Expr < ' _ > ,
1996
+ expr : & ' tcx Expr < ' _ > ,
1997
+ ) {
1998
+ if_chain ! {
1999
+ if let ExprKind :: Block ( ref block, _) = body. kind;
2000
+ if block. stmts. is_empty( ) ;
2001
+ if let Some ( inner_expr) = block. expr;
2002
+ if let ExprKind :: Match ( ref _match_expr, ref _match_arms, MatchSource :: IfLetDesugar { contains_else_clause } ) = inner_expr. kind;
2003
+ if !contains_else_clause;
2004
+ then {
2005
+ // println!("if_let_expr:\n{:?}", snippet(cx, if_let_expr.span, ".."));
2006
+ // println!("pat is:\n {:?}", snippet(cx, pat.span, ".."));
2007
+ // println!("arg is:\n {:?}", snippet(cx, arg.span, ".."));
2008
+ // println!("body is:\n {:?}", snippet(cx, body.span, ".."));
2009
+ // println!("arg kind is: {:?}", arg.kind);
2010
+ // println!("expr is:\n {:?}", snippet(cx, expr.span, ".."));
2011
+ // todo!();
2012
+ let arg_snippet = snippet( cx, arg. span, ".." ) ;
2013
+ let msg = "looping over `Option`s or `Result`s with an `if let` expression." ;
2014
+ let hint = format!( "try turn {} into an `Iterator` and use `flatten`: `{}.iter().flatten()`" , arg_snippet, arg_snippet) ;
2015
+ span_lint_and_help( cx, FOR_LOOPS_OVER_OPTIONS , expr. span, msg, None , & hint) ;
2016
+ }
2017
+ }
2018
+ }
2019
+
1956
2020
struct MutatePairDelegate < ' a , ' tcx > {
1957
2021
cx : & ' a LateContext < ' tcx > ,
1958
2022
hir_id_low : Option < HirId > ,
0 commit comments