11use super :: utils:: make_iterator_snippet;
22use super :: NEVER_LOOP ;
3- use clippy_utils:: consts:: { constant, Constant } ;
43use clippy_utils:: diagnostics:: span_lint_and_then;
54use clippy_utils:: higher:: ForLoop ;
65use clippy_utils:: source:: snippet;
@@ -18,7 +17,7 @@ pub(super) fn check<'tcx>(
1817 for_loop : Option < & ForLoop < ' _ > > ,
1918) {
2019 match never_loop_block ( cx, block, & mut Vec :: new ( ) , loop_id) {
21- NeverLoopResult :: AlwaysBreak => {
20+ NeverLoopResult :: Diverging => {
2221 span_lint_and_then ( cx, NEVER_LOOP , span, "this loop never actually loops" , |diag| {
2322 if let Some ( ForLoop {
2423 arg : iterator,
@@ -39,83 +38,98 @@ pub(super) fn check<'tcx>(
3938 }
4039 } ) ;
4140 } ,
42- NeverLoopResult :: MayContinueMainLoop | NeverLoopResult :: Otherwise => ( ) ,
43- NeverLoopResult :: IgnoreUntilEnd ( _) => unreachable ! ( ) ,
41+ NeverLoopResult :: MayContinueMainLoop | NeverLoopResult :: Normal => ( ) ,
4442 }
4543}
4644
45+ /// The `never_loop` analysis keeps track of three things:
46+ ///
47+ /// * Has any (reachable) code path hit a `continue` of the main loop?
48+ /// * Is the current code path diverging (that is, the next expression is not reachable)
49+ /// * For each block label `'a` inside the main loop, has any (reachable) code path encountered a
50+ /// `break 'a`?
51+ ///
52+ /// The first two bits of information are in this enum, and the last part is in the
53+ /// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by
54+ /// scope.
4755#[ derive( Copy , Clone ) ]
4856enum NeverLoopResult {
49- // A break/return always get triggered but not necessarily for the main loop.
50- AlwaysBreak ,
51- // A continue may occur for the main loop.
57+ /// A continue may occur for the main loop.
5258 MayContinueMainLoop ,
53- // Ignore everything until the end of the block with this id
54- IgnoreUntilEnd ( HirId ) ,
55- Otherwise ,
59+ /// We have not encountered any main loop continue,
60+ /// but we are diverging (subsequent control flow is not reachable)
61+ Diverging ,
62+ /// We have not encountered any main loop continue,
63+ /// and subsequent control flow is (possibly) reachable
64+ Normal ,
5665}
5766
5867#[ must_use]
5968fn absorb_break ( arg : NeverLoopResult ) -> NeverLoopResult {
6069 match arg {
61- NeverLoopResult :: AlwaysBreak | NeverLoopResult :: Otherwise => NeverLoopResult :: Otherwise ,
70+ NeverLoopResult :: Diverging | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
6271 NeverLoopResult :: MayContinueMainLoop => NeverLoopResult :: MayContinueMainLoop ,
63- NeverLoopResult :: IgnoreUntilEnd ( id) => NeverLoopResult :: IgnoreUntilEnd ( id) ,
6472 }
6573}
6674
6775// Combine two results for parts that are called in order.
6876#[ must_use]
69- fn combine_seq ( first : NeverLoopResult , second : NeverLoopResult ) -> NeverLoopResult {
77+ fn combine_seq ( first : NeverLoopResult , second : impl FnOnce ( ) -> NeverLoopResult ) -> NeverLoopResult {
7078 match first {
71- NeverLoopResult :: AlwaysBreak | NeverLoopResult :: MayContinueMainLoop | NeverLoopResult :: IgnoreUntilEnd ( _) => {
72- first
73- } ,
74- NeverLoopResult :: Otherwise => second,
79+ NeverLoopResult :: Diverging | NeverLoopResult :: MayContinueMainLoop => first,
80+ NeverLoopResult :: Normal => second ( ) ,
81+ }
82+ }
83+
84+ // Combine an iterator of results for parts that are called in order.
85+ #[ must_use]
86+ fn combine_seq_many ( iter : impl IntoIterator < Item = NeverLoopResult > ) -> NeverLoopResult {
87+ for e in iter {
88+ if let NeverLoopResult :: Diverging | NeverLoopResult :: MayContinueMainLoop = e {
89+ return e;
90+ }
7591 }
92+ NeverLoopResult :: Normal
7693}
7794
7895// Combine two results where only one of the part may have been executed.
7996#[ must_use]
80- fn combine_branches ( b1 : NeverLoopResult , b2 : NeverLoopResult , ignore_ids : & [ HirId ] ) -> NeverLoopResult {
97+ fn combine_branches ( b1 : NeverLoopResult , b2 : NeverLoopResult ) -> NeverLoopResult {
8198 match ( b1, b2) {
82- ( NeverLoopResult :: IgnoreUntilEnd ( a) , NeverLoopResult :: IgnoreUntilEnd ( b) ) => {
83- if ignore_ids. iter ( ) . find ( |& e| e == & a || e == & b) . unwrap ( ) == & a {
84- NeverLoopResult :: IgnoreUntilEnd ( b)
85- } else {
86- NeverLoopResult :: IgnoreUntilEnd ( a)
87- }
88- } ,
89- ( i @ NeverLoopResult :: IgnoreUntilEnd ( _) , NeverLoopResult :: AlwaysBreak )
90- | ( NeverLoopResult :: AlwaysBreak , i @ NeverLoopResult :: IgnoreUntilEnd ( _) ) => i,
91- ( NeverLoopResult :: AlwaysBreak , NeverLoopResult :: AlwaysBreak ) => NeverLoopResult :: AlwaysBreak ,
9299 ( NeverLoopResult :: MayContinueMainLoop , _) | ( _, NeverLoopResult :: MayContinueMainLoop ) => {
93100 NeverLoopResult :: MayContinueMainLoop
94101 } ,
95- ( NeverLoopResult :: Otherwise , _) | ( _, NeverLoopResult :: Otherwise ) => NeverLoopResult :: Otherwise ,
102+ ( NeverLoopResult :: Normal , _) | ( _, NeverLoopResult :: Normal ) => NeverLoopResult :: Normal ,
103+ ( NeverLoopResult :: Diverging , NeverLoopResult :: Diverging ) => NeverLoopResult :: Diverging ,
96104 }
97105}
98106
99107fn never_loop_block < ' tcx > (
100108 cx : & LateContext < ' tcx > ,
101109 block : & Block < ' tcx > ,
102- ignore_ids : & mut Vec < HirId > ,
110+ local_labels : & mut Vec < ( HirId , bool ) > ,
103111 main_loop_id : HirId ,
104112) -> NeverLoopResult {
105113 let iter = block
106114 . stmts
107115 . iter ( )
108116 . filter_map ( stmt_to_expr)
109117 . chain ( block. expr . map ( |expr| ( expr, None ) ) ) ;
110-
111- iter. map ( |( e, els) | {
112- let e = never_loop_expr ( cx, e, ignore_ids, main_loop_id) ;
118+ combine_seq_many ( iter. map ( |( e, els) | {
119+ let e = never_loop_expr ( cx, e, local_labels, main_loop_id) ;
113120 // els is an else block in a let...else binding
114121 els. map_or ( e, |els| {
115- combine_branches ( e, never_loop_block ( cx, els, ignore_ids, main_loop_id) , ignore_ids)
122+ combine_seq ( e, || match never_loop_block ( cx, els, local_labels, main_loop_id) {
123+ // Returning MayContinueMainLoop here means that
124+ // we will not evaluate the rest of the body
125+ NeverLoopResult :: MayContinueMainLoop => NeverLoopResult :: MayContinueMainLoop ,
126+ // An else block always diverges, so the Normal case should not happen,
127+ // but the analysis is approximate so it might return Normal anyway.
128+ // Returning Normal here says that nothing more happens on the main path
129+ NeverLoopResult :: Diverging | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
130+ } )
116131 } )
117- } )
118- . fold ( NeverLoopResult :: Otherwise , combine_seq)
132+ } ) )
119133}
120134
121135fn stmt_to_expr < ' tcx > ( stmt : & Stmt < ' tcx > ) -> Option < ( & ' tcx Expr < ' tcx > , Option < & ' tcx Block < ' tcx > > ) > {
@@ -131,7 +145,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
131145fn never_loop_expr < ' tcx > (
132146 cx : & LateContext < ' tcx > ,
133147 expr : & Expr < ' tcx > ,
134- ignore_ids : & mut Vec < HirId > ,
148+ local_labels : & mut Vec < ( HirId , bool ) > ,
135149 main_loop_id : HirId ,
136150) -> NeverLoopResult {
137151 match expr. kind {
@@ -141,66 +155,61 @@ fn never_loop_expr<'tcx>(
141155 | ExprKind :: Field ( e, _)
142156 | ExprKind :: AddrOf ( _, _, e)
143157 | ExprKind :: Repeat ( e, _)
144- | ExprKind :: DropTemps ( e) => never_loop_expr ( cx, e, ignore_ids , main_loop_id) ,
145- ExprKind :: Let ( let_expr) => never_loop_expr ( cx, let_expr. init , ignore_ids , main_loop_id) ,
146- ExprKind :: Array ( es) | ExprKind :: Tup ( es) => never_loop_expr_all ( cx, & mut es. iter ( ) , ignore_ids , main_loop_id) ,
158+ | ExprKind :: DropTemps ( e) => never_loop_expr ( cx, e, local_labels , main_loop_id) ,
159+ ExprKind :: Let ( let_expr) => never_loop_expr ( cx, let_expr. init , local_labels , main_loop_id) ,
160+ ExprKind :: Array ( es) | ExprKind :: Tup ( es) => never_loop_expr_all ( cx, es. iter ( ) , local_labels , main_loop_id) ,
147161 ExprKind :: MethodCall ( _, receiver, es, _) => never_loop_expr_all (
148162 cx,
149- & mut std:: iter:: once ( receiver) . chain ( es. iter ( ) ) ,
150- ignore_ids ,
163+ std:: iter:: once ( receiver) . chain ( es. iter ( ) ) ,
164+ local_labels ,
151165 main_loop_id,
152166 ) ,
153167 ExprKind :: Struct ( _, fields, base) => {
154- let fields = never_loop_expr_all ( cx, & mut fields. iter ( ) . map ( |f| f. expr ) , ignore_ids , main_loop_id) ;
168+ let fields = never_loop_expr_all ( cx, fields. iter ( ) . map ( |f| f. expr ) , local_labels , main_loop_id) ;
155169 if let Some ( base) = base {
156- combine_seq ( fields, never_loop_expr ( cx, base, ignore_ids , main_loop_id) )
170+ combine_seq ( fields, || never_loop_expr ( cx, base, local_labels , main_loop_id) )
157171 } else {
158172 fields
159173 }
160174 } ,
161- ExprKind :: Call ( e, es) => never_loop_expr_all ( cx, & mut once ( e) . chain ( es. iter ( ) ) , ignore_ids , main_loop_id) ,
175+ ExprKind :: Call ( e, es) => never_loop_expr_all ( cx, once ( e) . chain ( es. iter ( ) ) , local_labels , main_loop_id) ,
162176 ExprKind :: Binary ( _, e1, e2)
163177 | ExprKind :: Assign ( e1, e2, _)
164178 | ExprKind :: AssignOp ( _, e1, e2)
165- | ExprKind :: Index ( e1, e2, _) => {
166- never_loop_expr_all ( cx, & mut [ e1, e2] . iter ( ) . copied ( ) , ignore_ids, main_loop_id)
167- } ,
179+ | ExprKind :: Index ( e1, e2, _) => never_loop_expr_all ( cx, [ e1, e2] . iter ( ) . copied ( ) , local_labels, main_loop_id) ,
168180 ExprKind :: Loop ( b, _, _, _) => {
169- // Break can come from the inner loop so remove them.
170- absorb_break ( never_loop_block ( cx, b, ignore_ids, main_loop_id) )
181+ // We don't attempt to track reachability after a loop,
182+ // just assume there may have been a break somewhere
183+ absorb_break ( never_loop_block ( cx, b, local_labels, main_loop_id) )
171184 } ,
172185 ExprKind :: If ( e, e2, e3) => {
173- let e1 = never_loop_expr ( cx, e, ignore_ids, main_loop_id) ;
174- let e2 = never_loop_expr ( cx, e2, ignore_ids, main_loop_id) ;
175- // If we know the `if` condition evaluates to `true`, don't check everything past it; it
176- // should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable
177- if let Some ( Constant :: Bool ( true ) ) = constant ( cx, cx. typeck_results ( ) , e) {
178- return combine_seq ( e1, e2) ;
179- }
180- let e3 = e3. as_ref ( ) . map_or ( NeverLoopResult :: Otherwise , |e| {
181- never_loop_expr ( cx, e, ignore_ids, main_loop_id)
182- } ) ;
183- combine_seq ( e1, combine_branches ( e2, e3, ignore_ids) )
186+ let e1 = never_loop_expr ( cx, e, local_labels, main_loop_id) ;
187+ combine_seq ( e1, || {
188+ let e2 = never_loop_expr ( cx, e2, local_labels, main_loop_id) ;
189+ let e3 = e3. as_ref ( ) . map_or ( NeverLoopResult :: Normal , |e| {
190+ never_loop_expr ( cx, e, local_labels, main_loop_id)
191+ } ) ;
192+ combine_branches ( e2, e3)
193+ } )
184194 } ,
185195 ExprKind :: Match ( e, arms, _) => {
186- let e = never_loop_expr ( cx, e, ignore_ids , main_loop_id) ;
196+ let e = never_loop_expr ( cx, e, local_labels , main_loop_id) ;
187197 if arms. is_empty ( ) {
188198 e
189199 } else {
190- let arms = never_loop_expr_branch ( cx, & mut arms. iter ( ) . map ( |a| a. body ) , ignore_ids, main_loop_id) ;
191- combine_seq ( e, arms)
200+ combine_seq ( e, || {
201+ never_loop_expr_branch ( cx, & mut arms. iter ( ) . map ( |a| a. body ) , local_labels, main_loop_id)
202+ } )
192203 }
193204 } ,
194205 ExprKind :: Block ( b, l) => {
195206 if l. is_some ( ) {
196- ignore_ids. push ( b. hir_id ) ;
197- }
198- let ret = never_loop_block ( cx, b, ignore_ids, main_loop_id) ;
199- if l. is_some ( ) {
200- ignore_ids. pop ( ) ;
207+ local_labels. push ( ( b. hir_id , false ) ) ;
201208 }
209+ let ret = never_loop_block ( cx, b, local_labels, main_loop_id) ;
210+ let jumped_to = l. is_some ( ) && local_labels. pop ( ) . unwrap ( ) . 1 ;
202211 match ret {
203- NeverLoopResult :: IgnoreUntilEnd ( a ) if a == b . hir_id = > NeverLoopResult :: Otherwise ,
212+ NeverLoopResult :: Diverging if jumped_to = > NeverLoopResult :: Normal ,
204213 _ => ret,
205214 }
206215 } ,
@@ -211,73 +220,70 @@ fn never_loop_expr<'tcx>(
211220 if id == main_loop_id {
212221 NeverLoopResult :: MayContinueMainLoop
213222 } else {
214- NeverLoopResult :: AlwaysBreak
223+ NeverLoopResult :: Diverging
215224 }
216225 } ,
217- // checks if break targets a block instead of a loop
218- ExprKind :: Break ( Destination { target_id : Ok ( t) , .. } , e) if ignore_ids. contains ( & t) => e
219- . map_or ( NeverLoopResult :: IgnoreUntilEnd ( t) , |e| {
220- never_loop_expr ( cx, e, ignore_ids, main_loop_id)
221- } ) ,
222- ExprKind :: Break ( _, e) | ExprKind :: Ret ( e) => e. as_ref ( ) . map_or ( NeverLoopResult :: AlwaysBreak , |e| {
223- combine_seq (
224- never_loop_expr ( cx, e, ignore_ids, main_loop_id) ,
225- NeverLoopResult :: AlwaysBreak ,
226- )
227- } ) ,
228- ExprKind :: Become ( e) => combine_seq (
229- never_loop_expr ( cx, e, ignore_ids, main_loop_id) ,
230- NeverLoopResult :: AlwaysBreak ,
231- ) ,
232- ExprKind :: InlineAsm ( asm) => asm
233- . operands
234- . iter ( )
235- . map ( |( o, _) | match o {
236- InlineAsmOperand :: In { expr, .. } | InlineAsmOperand :: InOut { expr, .. } => {
237- never_loop_expr ( cx, expr, ignore_ids, main_loop_id)
238- } ,
239- InlineAsmOperand :: Out { expr, .. } => {
240- never_loop_expr_all ( cx, & mut expr. iter ( ) . copied ( ) , ignore_ids, main_loop_id)
241- } ,
242- InlineAsmOperand :: SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all (
243- cx,
244- & mut once ( * in_expr) . chain ( out_expr. iter ( ) . copied ( ) ) ,
245- ignore_ids,
246- main_loop_id,
247- ) ,
248- InlineAsmOperand :: Const { .. }
249- | InlineAsmOperand :: SymFn { .. }
250- | InlineAsmOperand :: SymStatic { .. } => NeverLoopResult :: Otherwise ,
226+ ExprKind :: Break ( _, e) | ExprKind :: Ret ( e) => {
227+ let first = e. as_ref ( ) . map_or ( NeverLoopResult :: Normal , |e| {
228+ never_loop_expr ( cx, e, local_labels, main_loop_id)
229+ } ) ;
230+ combine_seq ( first, || {
231+ // checks if break targets a block instead of a loop
232+ if let ExprKind :: Break ( Destination { target_id : Ok ( t) , .. } , _) = expr. kind {
233+ if let Some ( ( _, reachable) ) = local_labels. iter_mut ( ) . find ( |( label, _) | * label == t) {
234+ * reachable = true ;
235+ }
236+ }
237+ NeverLoopResult :: Diverging
251238 } )
252- . fold ( NeverLoopResult :: Otherwise , combine_seq) ,
239+ } ,
240+ ExprKind :: Become ( e) => combine_seq ( never_loop_expr ( cx, e, local_labels, main_loop_id) , || {
241+ NeverLoopResult :: Diverging
242+ } ) ,
243+ ExprKind :: InlineAsm ( asm) => combine_seq_many ( asm. operands . iter ( ) . map ( |( o, _) | match o {
244+ InlineAsmOperand :: In { expr, .. } | InlineAsmOperand :: InOut { expr, .. } => {
245+ never_loop_expr ( cx, expr, local_labels, main_loop_id)
246+ } ,
247+ InlineAsmOperand :: Out { expr, .. } => {
248+ never_loop_expr_all ( cx, expr. iter ( ) . copied ( ) , local_labels, main_loop_id)
249+ } ,
250+ InlineAsmOperand :: SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all (
251+ cx,
252+ once ( * in_expr) . chain ( out_expr. iter ( ) . copied ( ) ) ,
253+ local_labels,
254+ main_loop_id,
255+ ) ,
256+ InlineAsmOperand :: Const { .. } | InlineAsmOperand :: SymFn { .. } | InlineAsmOperand :: SymStatic { .. } => {
257+ NeverLoopResult :: Normal
258+ } ,
259+ } ) ) ,
253260 ExprKind :: OffsetOf ( _, _)
254261 | ExprKind :: Yield ( _, _)
255262 | ExprKind :: Closure { .. }
256263 | ExprKind :: Path ( _)
257264 | ExprKind :: ConstBlock ( _)
258265 | ExprKind :: Lit ( _)
259- | ExprKind :: Err ( _) => NeverLoopResult :: Otherwise ,
266+ | ExprKind :: Err ( _) => NeverLoopResult :: Normal ,
260267 }
261268}
262269
263270fn never_loop_expr_all < ' tcx , T : Iterator < Item = & ' tcx Expr < ' tcx > > > (
264271 cx : & LateContext < ' tcx > ,
265- es : & mut T ,
266- ignore_ids : & mut Vec < HirId > ,
272+ es : T ,
273+ local_labels : & mut Vec < ( HirId , bool ) > ,
267274 main_loop_id : HirId ,
268275) -> NeverLoopResult {
269- es. map ( |e| never_loop_expr ( cx, e, ignore_ids, main_loop_id) )
270- . fold ( NeverLoopResult :: Otherwise , combine_seq)
276+ combine_seq_many ( es. map ( |e| never_loop_expr ( cx, e, local_labels, main_loop_id) ) )
271277}
272278
273279fn never_loop_expr_branch < ' tcx , T : Iterator < Item = & ' tcx Expr < ' tcx > > > (
274280 cx : & LateContext < ' tcx > ,
275281 e : & mut T ,
276- ignore_ids : & mut Vec < HirId > ,
282+ local_labels : & mut Vec < ( HirId , bool ) > ,
277283 main_loop_id : HirId ,
278284) -> NeverLoopResult {
279- e. fold ( NeverLoopResult :: AlwaysBreak , |a, b| {
280- combine_branches ( a, never_loop_expr ( cx, b, ignore_ids , main_loop_id) , ignore_ids )
285+ e. fold ( NeverLoopResult :: Diverging , |a, b| {
286+ combine_branches ( a, never_loop_expr ( cx, b, local_labels , main_loop_id) )
281287 } )
282288}
283289
0 commit comments