@@ -1996,52 +1996,82 @@ fn check_manual_flatten<'tcx>(
19961996 body : & ' tcx Expr < ' _ > ,
19971997 span : Span ,
19981998) {
1999- if_chain ! {
2000- // Ensure the `if let` statement is the only expression in the for-loop
2001- if let ExprKind :: Block ( ref block, _) = body. kind;
2002- if block. stmts. is_empty( ) ;
2003- if let Some ( inner_expr) = block. expr;
2004- if let ExprKind :: Match (
2005- ref match_expr, ref match_arms, MatchSource :: IfLetDesugar { contains_else_clause: false }
2006- ) = inner_expr. kind;
2007- // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
2008- if let PatKind :: Binding ( _, pat_hir_id, _, _) = pat. kind;
2009- if let ExprKind :: Path ( QPath :: Resolved ( None , match_expr_path) ) = match_expr. kind;
2010- if let Res :: Local ( match_expr_path_id) = match_expr_path. res;
2011- if pat_hir_id == match_expr_path_id;
2012- // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
2013- if let PatKind :: TupleStruct ( QPath :: Resolved ( None , path) , _, _) = match_arms[ 0 ] . pat. kind;
2014- if is_some_ctor( cx, path. res) || is_ok_ctor( cx, path. res) ;
2015- let if_let_type = if is_some_ctor( cx, path. res) {
2016- "Some"
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
20172010 } else {
2018- "Ok"
2011+ None
20192012 } ;
2020- // Determine if `arg` is `Iterator` or implicitly calls `into_iter`
2021- let arg_ty = cx. typeck_results( ) . expr_ty( arg) ;
2022- if let Some ( id) = get_trait_def_id( cx, & paths:: ITERATOR ) ;
2023- if let is_iterator = implements_trait( cx, arg_ty, id, & [ ] ) ;
20242013
2025- then {
2026- // Prepare the error message
2027- let msg = format!( "Unnecessary `if let` since only the `{}` variant of the iterator element is used." , if_let_type) ;
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" } ;
20282030
2029- // Prepare the help message
2030- let arg_snippet = snippet( cx, arg. span, ".." ) ;
2031- let hint = if is_iterator {
2032- format!( "try: `{}.flatten()`" , arg_snippet)
2033- } else {
2034- format!( "try: `{}.into_iter().flatten()`" , arg_snippet)
2035- } ;
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) ;
20362034
2037- span_lint_and_help(
2038- cx,
2039- MANUAL_FLATTEN ,
2040- span,
2041- & msg,
2042- Some ( arg. span) ,
2043- & hint,
2044- ) ;
2035+ // Prepare the help message
2036+ let mut applicability = Applicability :: MaybeIncorrect ;
2037+ let arg_snippet = snippet_with_applicability(
2038+ cx,
2039+ arg. span,
2040+ ".." ,
2041+ & mut applicability,
2042+ ) ;
2043+ // Determine if `arg` is by reference, an `Iterator`, or implicitly adjusted with `into_iter`
2044+ let hint = match arg. kind {
2045+ ExprKind :: AddrOf ( _, _, arg_expr) => {
2046+ format!( "{}.iter().flatten()" , snippet( cx, arg_expr. span, ".." ) )
2047+ } ,
2048+ ExprKind :: MethodCall ( _, _, _, _) | ExprKind :: Path ( QPath :: Resolved ( None , _) ) => {
2049+ // Determine if `arg` is `Iterator` or implicitly calls `into_iter`
2050+ let arg_ty = cx. typeck_results( ) . expr_ty( arg) ;
2051+ if let Some ( id) = get_trait_def_id( cx, & paths:: ITERATOR ) {
2052+ let is_iterator = implements_trait( cx, arg_ty, id, & [ ] ) ;
2053+ if is_iterator {
2054+ format!( "{}.flatten()" , arg_snippet)
2055+ } else {
2056+ format!( "{}.into_iter().flatten()" , arg_snippet)
2057+ }
2058+ } else {
2059+ return
2060+ }
2061+ } ,
2062+ _ => return ,
2063+ } ;
2064+
2065+ span_lint_and_sugg(
2066+ cx,
2067+ MANUAL_FLATTEN ,
2068+ span,
2069+ & msg,
2070+ "try" ,
2071+ hint,
2072+ applicability,
2073+ )
2074+ }
20452075 }
20462076 }
20472077}
0 commit comments