@@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
4
4
use clippy_utils:: { fn_def_id, is_from_proc_macro, is_lint_allowed} ;
5
5
use hir:: intravisit:: { walk_expr, Visitor } ;
6
6
use hir:: { ClosureKind , Expr , ExprKind , FnRetTy , FnSig , ItemKind , Node , Ty , TyKind } ;
7
- use rustc_ast:: Label ;
8
7
use rustc_errors:: Applicability ;
9
8
use rustc_hir as hir;
10
9
use rustc_lint:: { LateContext , LintContext } ;
@@ -17,7 +16,7 @@ pub(super) fn check<'tcx>(
17
16
cx : & LateContext < ' tcx > ,
18
17
expr : & Expr < ' tcx > ,
19
18
loop_block : & ' tcx hir:: Block < ' _ > ,
20
- label : Option < Label > ,
19
+ hir_id : hir :: HirId ,
21
20
) {
22
21
if is_lint_allowed ( cx, INFINITE_LOOP , expr. hir_id ) {
23
22
return ;
@@ -33,15 +32,11 @@ pub(super) fn check<'tcx>(
33
32
34
33
let mut loop_visitor = LoopVisitor {
35
34
cx,
36
- label,
37
- is_finite : false ,
38
- loop_depth : 0 ,
35
+ hir_id,
36
+ nested_loop_count : 0 ,
39
37
} ;
40
- loop_visitor. visit_block ( loop_block) ;
41
-
42
- let is_finite_loop = loop_visitor. is_finite ;
43
-
44
- if !is_finite_loop {
38
+ let is_finite = loop_visitor. visit_block ( loop_block) . is_break ( ) ;
39
+ if !is_finite {
45
40
span_lint_and_then ( cx, INFINITE_LOOP , expr. span , "infinite loop detected" , |diag| {
46
41
if let Some ( span) = parent_fn_ret. sugg_span ( ) {
47
42
// Check if the type span is after a `->` or not.
@@ -106,40 +101,43 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option
106
101
107
102
struct LoopVisitor < ' hir , ' tcx > {
108
103
cx : & ' hir LateContext < ' tcx > ,
109
- label : Option < Label > ,
110
- loop_depth : usize ,
111
- is_finite : bool ,
104
+ // `HirId` for the entry (outer most) loop.
105
+ hir_id : hir :: HirId ,
106
+ nested_loop_count : usize ,
112
107
}
113
108
114
109
impl < ' hir > Visitor < ' hir > for LoopVisitor < ' hir , ' _ > {
115
- fn visit_expr ( & mut self , ex : & ' hir Expr < ' _ > ) {
110
+ type Result = ControlFlow < ( ) > ;
111
+
112
+ fn visit_expr ( & mut self , ex : & ' hir Expr < ' _ > ) -> Self :: Result {
116
113
match & ex. kind {
117
- ExprKind :: Break ( hir:: Destination { label, .. } , ..) => {
118
- // Assuming breaks the loop when `loop_depth` is 0,
119
- // as it could only means this `break` breaks current loop or any of its upper loop.
120
- // Or, the depth is not zero but the label is matched.
121
- if self . loop_depth == 0 || ( label. is_some ( ) && * label == self . label ) {
122
- self . is_finite = true ;
114
+ ExprKind :: Break ( hir:: Destination { target_id, .. } , ..) => {
115
+ // When there are no nested loops and we've encountered a `break`, meaning it's either breaking the
116
+ // current loop or any of the parent loop, assuming that this loop is not infinite.
117
+ if self . nested_loop_count == 0
118
+ || matches ! ( target_id, Ok ( target_loop_hid) if * target_loop_hid == self . hir_id)
119
+ {
120
+ return ControlFlow :: Break ( ( ) ) ;
123
121
}
124
122
} ,
125
- ExprKind :: Ret ( ..) => self . is_finite = true ,
123
+ ExprKind :: Ret ( ..) => return ControlFlow :: Break ( ( ) ) ,
126
124
ExprKind :: Loop ( ..) => {
127
- self . loop_depth += 1 ;
128
- walk_expr ( self , ex) ;
129
- self . loop_depth = self . loop_depth . saturating_sub ( 1 ) ;
125
+ self . nested_loop_count += 1 ;
126
+ let cf = walk_expr ( self , ex) ;
127
+ self . nested_loop_count = self . nested_loop_count . saturating_sub ( 1 ) ;
128
+ return cf;
130
129
} ,
131
130
_ => {
132
131
// Calls to a function that never return
133
132
if let Some ( did) = fn_def_id ( self . cx , ex) {
134
133
let fn_ret_ty = self . cx . tcx . fn_sig ( did) . skip_binder ( ) . output ( ) . skip_binder ( ) ;
135
134
if fn_ret_ty. is_never ( ) {
136
- self . is_finite = true ;
137
- return ;
135
+ return ControlFlow :: Break ( ( ) ) ;
138
136
}
139
137
}
140
- walk_expr ( self , ex) ;
141
138
} ,
142
139
}
140
+ walk_expr ( self , ex)
143
141
}
144
142
}
145
143
0 commit comments