@@ -5,10 +5,10 @@ use crate::utils::usage::{is_unused, mutated_variables};
55use crate :: utils:: visitors:: LocalUsedVisitor ;
66use crate :: utils:: {
77 contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
8- indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item ,
9- last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, single_segment_path , snippet ,
10- snippet_with_applicability , snippet_with_macro_callsite , span_lint , span_lint_and_help , span_lint_and_sugg ,
11- span_lint_and_then, sugg, SpanlessEq ,
8+ indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_ok_ctor , is_refutable, is_some_ctor ,
9+ is_type_diagnostic_item , last_path_segment, match_trait_method, match_type, match_var, multispan_sugg,
10+ single_segment_path , snippet , snippet_with_applicability , snippet_with_macro_callsite , span_lint ,
11+ span_lint_and_help , span_lint_and_sugg , span_lint_and_then, sugg, SpanlessEq ,
1212} ;
1313use if_chain:: if_chain;
1414use rustc_ast:: ast;
@@ -494,8 +494,40 @@ declare_clippy_lint! {
494494 "there is no reason to have a single element loop"
495495}
496496
497+ declare_clippy_lint ! {
498+ /// **What it does:** Check for unnecessary `if let` usage in a for loop
499+ /// where only the `Some` or `Ok` variant of the iterator element is used.
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.into_iter().flatten() {
520+ /// println!("{}", n);
521+ /// }
522+ /// ```
523+ pub MANUAL_FLATTEN ,
524+ complexity,
525+ "for loops over `Option`s or `Result`s with a single expression can be simplified"
526+ }
527+
497528declare_lint_pass ! ( Loops => [
498529 MANUAL_MEMCPY ,
530+ MANUAL_FLATTEN ,
499531 NEEDLESS_RANGE_LOOP ,
500532 EXPLICIT_ITER_LOOP ,
501533 EXPLICIT_INTO_ITER_LOOP ,
@@ -517,14 +549,14 @@ declare_lint_pass!(Loops => [
517549impl < ' tcx > LateLintPass < ' tcx > for Loops {
518550 #[ allow( clippy:: too_many_lines) ]
519551 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
520- if let Some ( ( pat, arg, body) ) = higher:: for_loop ( expr) {
552+ if let Some ( ( pat, arg, body, span ) ) = higher:: for_loop ( expr) {
521553 // we don't want to check expanded macros
522554 // this check is not at the top of the function
523555 // since higher::for_loop expressions are marked as expansions
524556 if body. span . from_expansion ( ) {
525557 return ;
526558 }
527- check_for_loop ( cx, pat, arg, body, expr) ;
559+ check_for_loop ( cx, pat, arg, body, expr, span ) ;
528560 }
529561
530562 // we don't want to check expanded macros
@@ -819,6 +851,7 @@ fn check_for_loop<'tcx>(
819851 arg : & ' tcx Expr < ' _ > ,
820852 body : & ' tcx Expr < ' _ > ,
821853 expr : & ' tcx Expr < ' _ > ,
854+ span : Span ,
822855) {
823856 let is_manual_memcpy_triggered = detect_manual_memcpy ( cx, pat, arg, body, expr) ;
824857 if !is_manual_memcpy_triggered {
@@ -830,6 +863,7 @@ fn check_for_loop<'tcx>(
830863 check_for_mut_range_bound ( cx, arg, body) ;
831864 check_for_single_element_loop ( cx, pat, arg, body, expr) ;
832865 detect_same_item_push ( cx, pat, arg, body, expr) ;
866+ check_manual_flatten ( cx, pat, arg, body, span) ;
833867}
834868
835869// this function assumes the given expression is a `for` loop.
@@ -1953,6 +1987,79 @@ fn check_for_single_element_loop<'tcx>(
19531987 }
19541988}
19551989
1990+ /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
1991+ /// iterator element is used.
1992+ fn check_manual_flatten < ' tcx > (
1993+ cx : & LateContext < ' tcx > ,
1994+ pat : & ' tcx Pat < ' _ > ,
1995+ arg : & ' tcx Expr < ' _ > ,
1996+ body : & ' tcx Expr < ' _ > ,
1997+ span : Span ,
1998+ ) {
1999+ if let ExprKind :: Block ( ref block, _) = body. kind {
2000+ // Ensure the `if let` statement is the only expression or statement in the for-loop
2001+ let inner_expr = if block. stmts . len ( ) == 1 && block. expr . is_none ( ) {
2002+ let match_stmt = & block. stmts [ 0 ] ;
2003+ if let StmtKind :: Semi ( inner_expr) = match_stmt. kind {
2004+ Some ( inner_expr)
2005+ } else {
2006+ None
2007+ }
2008+ } else if block. stmts . is_empty ( ) {
2009+ block. expr
2010+ } else {
2011+ None
2012+ } ;
2013+
2014+ if_chain ! {
2015+ if let Some ( inner_expr) = inner_expr;
2016+ if let ExprKind :: Match (
2017+ ref match_expr, ref match_arms, MatchSource :: IfLetDesugar { contains_else_clause: false }
2018+ ) = inner_expr. kind;
2019+ // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
2020+ if let PatKind :: Binding ( _, pat_hir_id, _, _) = pat. kind;
2021+ if let ExprKind :: Path ( QPath :: Resolved ( None , match_expr_path) ) = match_expr. kind;
2022+ if let Res :: Local ( match_expr_path_id) = match_expr_path. res;
2023+ if pat_hir_id == match_expr_path_id;
2024+ // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
2025+ if let PatKind :: TupleStruct ( QPath :: Resolved ( None , path) , _, _) = match_arms[ 0 ] . pat. kind;
2026+ let some_ctor = is_some_ctor( cx, path. res) ;
2027+ let ok_ctor = is_ok_ctor( cx, path. res) ;
2028+ if some_ctor || ok_ctor;
2029+ let if_let_type = if some_ctor { "Some" } else { "Ok" } ;
2030+
2031+ then {
2032+ // Prepare the error message
2033+ let msg = format!( "unnecessary `if let` since only the `{}` variant of the iterator element is used" , if_let_type) ;
2034+
2035+ // Prepare the help message
2036+ let mut applicability = Applicability :: MaybeIncorrect ;
2037+ let arg_snippet = make_iterator_snippet( cx, arg, & mut applicability) ;
2038+
2039+ span_lint_and_then(
2040+ cx,
2041+ MANUAL_FLATTEN ,
2042+ span,
2043+ & msg,
2044+ |diag| {
2045+ let sugg = format!( "{}.flatten()" , arg_snippet) ;
2046+ diag. span_suggestion(
2047+ arg. span,
2048+ "try" ,
2049+ sugg,
2050+ Applicability :: MaybeIncorrect ,
2051+ ) ;
2052+ diag. span_help(
2053+ inner_expr. span,
2054+ "...and remove the `if let` statement in the for loop" ,
2055+ ) ;
2056+ }
2057+ ) ;
2058+ }
2059+ }
2060+ }
2061+ }
2062+
19562063struct MutatePairDelegate < ' a , ' tcx > {
19572064 cx : & ' a LateContext < ' tcx > ,
19582065 hir_id_low : Option < HirId > ,
0 commit comments