@@ -2,71 +2,60 @@ use super::WHILE_LET_LOOP;
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
3
use clippy_utils:: higher;
4
4
use clippy_utils:: source:: snippet_with_applicability;
5
+ use clippy_utils:: ty:: needs_ordered_drop;
6
+ use clippy_utils:: visitors:: any_temporaries_need_ordered_drop;
5
7
use 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 ;
9
10
10
11
pub ( 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 ;
24
18
}
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
+ } ;
38
23
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) ;
46
35
}
47
- None
48
36
}
49
37
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( ) )
60
41
}
61
42
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
70
59
}
71
60
}
72
61
@@ -75,8 +64,13 @@ fn could_be_while_let<'tcx>(
75
64
expr : & ' tcx Expr < ' _ > ,
76
65
let_pat : & ' tcx Pat < ' _ > ,
77
66
let_expr : & ' tcx Expr < ' _ > ,
67
+ has_trailing_exprs : bool ,
78
68
) {
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.
80
74
return ;
81
75
}
82
76
0 commit comments