@@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
44use clippy_utils:: { fn_def_id, is_from_proc_macro, is_lint_allowed} ;
55use hir:: intravisit:: { walk_expr, Visitor } ;
66use hir:: { ClosureKind , Expr , ExprKind , FnRetTy , FnSig , ItemKind , Node , Ty , TyKind } ;
7- use rustc_ast:: Label ;
87use rustc_errors:: Applicability ;
98use rustc_hir as hir;
109use rustc_lint:: { LateContext , LintContext } ;
@@ -17,7 +16,7 @@ pub(super) fn check<'tcx>(
1716 cx : & LateContext < ' tcx > ,
1817 expr : & Expr < ' tcx > ,
1918 loop_block : & ' tcx hir:: Block < ' _ > ,
20- label : Option < Label > ,
19+ hir_id : hir :: HirId ,
2120) {
2221 if is_lint_allowed ( cx, INFINITE_LOOP , expr. hir_id ) {
2322 return ;
@@ -33,15 +32,11 @@ pub(super) fn check<'tcx>(
3332
3433 let mut loop_visitor = LoopVisitor {
3534 cx,
36- label,
37- is_finite : false ,
38- loop_depth : 0 ,
35+ hir_id,
36+ nested_loop_count : 0 ,
3937 } ;
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 {
4540 span_lint_and_then ( cx, INFINITE_LOOP , expr. span , "infinite loop detected" , |diag| {
4641 if let Some ( span) = parent_fn_ret. sugg_span ( ) {
4742 // 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
106101
107102struct LoopVisitor < ' hir , ' tcx > {
108103 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 ,
112107}
113108
114109impl < ' 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 {
116113 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 ( ( ) ) ;
123121 }
124122 } ,
125- ExprKind :: Ret ( ..) => self . is_finite = true ,
123+ ExprKind :: Ret ( ..) => return ControlFlow :: Break ( ( ) ) ,
126124 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;
130129 } ,
131130 _ => {
132131 // Calls to a function that never return
133132 if let Some ( did) = fn_def_id ( self . cx , ex) {
134133 let fn_ret_ty = self . cx . tcx . fn_sig ( did) . skip_binder ( ) . output ( ) . skip_binder ( ) ;
135134 if fn_ret_ty. is_never ( ) {
136- self . is_finite = true ;
137- return ;
135+ return ControlFlow :: Break ( ( ) ) ;
138136 }
139137 }
140- walk_expr ( self , ex) ;
141138 } ,
142139 }
140+ walk_expr ( self , ex)
143141 }
144142}
145143
0 commit comments