@@ -2,71 +2,60 @@ use super::WHILE_LET_LOOP;
22use clippy_utils:: diagnostics:: span_lint_and_sugg;
33use clippy_utils:: higher;
44use clippy_utils:: source:: snippet_with_applicability;
5+ use clippy_utils:: ty:: needs_ordered_drop;
6+ use clippy_utils:: visitors:: any_temporaries_need_ordered_drop;
57use rustc_errors:: Applicability ;
6- use rustc_hir:: { Block , Expr , ExprKind , MatchSource , Pat , StmtKind } ;
7- use rustc_lint:: { LateContext , LintContext } ;
8- use rustc_middle:: lint:: in_external_macro;
8+ use rustc_hir:: { Block , Expr , ExprKind , Local , MatchSource , Pat , StmtKind } ;
9+ use rustc_lint:: LateContext ;
910
1011pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , loop_block : & ' tcx Block < ' _ > ) {
11- // extract the expression from the first statement (if any) in a block
12- let inner_stmt_expr = extract_expr_from_first_stmt ( loop_block) ;
13- // or extract the first expression (if any) from the block
14- if let Some ( inner) = inner_stmt_expr. or_else ( || extract_first_expr ( loop_block) ) {
15- if let Some ( higher:: IfLet {
16- let_pat,
17- let_expr,
18- if_else : Some ( if_else) ,
19- ..
20- } ) = higher:: IfLet :: hir ( cx, inner)
21- {
22- if is_simple_break_expr ( if_else) {
23- could_be_while_let ( cx, expr, let_pat, let_expr) ;
12+ let ( init, has_trailing_exprs) = match ( loop_block. stmts , loop_block. expr ) {
13+ ( [ stmt, stmts @ ..] , expr) => {
14+ if let StmtKind :: Local ( & Local { init : Some ( e) , .. } ) | StmtKind :: Semi ( e) | StmtKind :: Expr ( e) = stmt. kind {
15+ ( e, !stmts. is_empty ( ) || expr. is_some ( ) )
16+ } else {
17+ return ;
2418 }
25- }
26-
27- if let ExprKind :: Match ( matchexpr, arms, MatchSource :: Normal ) = inner. kind {
28- if arms. len ( ) == 2
29- && arms[ 0 ] . guard . is_none ( )
30- && arms[ 1 ] . guard . is_none ( )
31- && is_simple_break_expr ( arms[ 1 ] . body )
32- {
33- could_be_while_let ( cx, expr, arms[ 0 ] . pat , matchexpr) ;
34- }
35- }
36- }
37- }
19+ } ,
20+ ( [ ] , Some ( e) ) => ( e, false ) ,
21+ _ => return ,
22+ } ;
3823
39- /// If a block begins with a statement (possibly a `let` binding) and has an
40- /// expression, return it.
41- fn extract_expr_from_first_stmt < ' tcx > ( block : & Block < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > {
42- if let Some ( first_stmt) = block. stmts . get ( 0 ) {
43- if let StmtKind :: Local ( local) = first_stmt. kind {
44- return local. init ;
45- }
24+ if let Some ( if_let) = higher:: IfLet :: hir ( cx, init)
25+ && let Some ( else_expr) = if_let. if_else
26+ && is_simple_break_expr ( else_expr)
27+ {
28+ could_be_while_let ( cx, expr, if_let. let_pat , if_let. let_expr , has_trailing_exprs) ;
29+ } else if let ExprKind :: Match ( scrutinee, [ arm1, arm2] , MatchSource :: Normal ) = init. kind
30+ && arm1. guard . is_none ( )
31+ && arm2. guard . is_none ( )
32+ && is_simple_break_expr ( arm2. body )
33+ {
34+ could_be_while_let ( cx, expr, arm1. pat , scrutinee, has_trailing_exprs) ;
4635 }
47- None
4836}
4937
50- /// If a block begins with an expression (with or without semicolon), return it.
51- fn extract_first_expr < ' tcx > ( block : & Block < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > {
52- match block. expr {
53- Some ( expr) if block. stmts . is_empty ( ) => Some ( expr) ,
54- None if !block. stmts . is_empty ( ) => match block. stmts [ 0 ] . kind {
55- StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => Some ( expr) ,
56- StmtKind :: Local ( ..) | StmtKind :: Item ( ..) => None ,
57- } ,
58- _ => None ,
59- }
38+ /// Returns `true` if expr contains a single break expression without a label or eub-expression.
39+ fn is_simple_break_expr ( e : & Expr < ' _ > ) -> bool {
40+ matches ! ( peel_blocks( e) . kind, ExprKind :: Break ( dest, None ) if dest. label. is_none( ) )
6041}
6142
62- /// Returns `true` if expr contains a single break expr without destination label
63- /// and
64- /// passed expression. The expression may be within a block.
65- fn is_simple_break_expr ( expr : & Expr < ' _ > ) -> bool {
66- match expr. kind {
67- ExprKind :: Break ( dest, ref passed_expr) if dest. label . is_none ( ) && passed_expr. is_none ( ) => true ,
68- ExprKind :: Block ( b, _) => extract_first_expr ( b) . map_or ( false , is_simple_break_expr) ,
69- _ => false ,
43+ /// Removes any blocks containing only a single expression.
44+ fn peel_blocks < ' tcx > ( e : & ' tcx Expr < ' tcx > ) -> & ' tcx Expr < ' tcx > {
45+ if let ExprKind :: Block ( b, _) = e. kind {
46+ match ( b. stmts , b. expr ) {
47+ ( [ s] , None ) => {
48+ if let StmtKind :: Expr ( e) | StmtKind :: Semi ( e) = s. kind {
49+ peel_blocks ( e)
50+ } else {
51+ e
52+ }
53+ } ,
54+ ( [ ] , Some ( e) ) => peel_blocks ( e) ,
55+ _ => e,
56+ }
57+ } else {
58+ e
7059 }
7160}
7261
@@ -75,8 +64,13 @@ fn could_be_while_let<'tcx>(
7564 expr : & ' tcx Expr < ' _ > ,
7665 let_pat : & ' tcx Pat < ' _ > ,
7766 let_expr : & ' tcx Expr < ' _ > ,
67+ has_trailing_exprs : bool ,
7868) {
79- if in_external_macro ( cx. sess ( ) , expr. span ) {
69+ if has_trailing_exprs
70+ && ( needs_ordered_drop ( cx, cx. typeck_results ( ) . expr_ty ( let_expr) )
71+ || any_temporaries_need_ordered_drop ( cx, let_expr) )
72+ {
73+ // Switching to a `while let` loop will extend the lifetime of some values.
8074 return ;
8175 }
8276
0 commit comments