@@ -6,9 +6,11 @@ use clippy_utils::macros::root_macro_call_first_node;
66use clippy_utils:: source:: snippet;
77use clippy_utils:: visitors:: { Descend , for_each_expr_without_closures} ;
88use rustc_errors:: Applicability ;
9- use rustc_hir:: { Block , Destination , Expr , ExprKind , HirId , InlineAsmOperand , Pat , Stmt , StmtKind , StructTailExpr } ;
9+ use rustc_hir:: {
10+ Block , Destination , Expr , ExprKind , HirId , InlineAsmOperand , Node , Pat , Stmt , StmtKind , StructTailExpr ,
11+ } ;
1012use rustc_lint:: LateContext ;
11- use rustc_span:: { Span , sym} ;
13+ use rustc_span:: { BytePos , Span , sym} ;
1214use std:: iter:: once;
1315use std:: ops:: ControlFlow ;
1416
@@ -20,7 +22,7 @@ pub(super) fn check<'tcx>(
2022 for_loop : Option < & ForLoop < ' _ > > ,
2123) {
2224 match never_loop_block ( cx, block, & mut Vec :: new ( ) , loop_id) {
23- NeverLoopResult :: Diverging => {
25+ NeverLoopResult :: Diverging { ref break_spans } => {
2426 span_lint_and_then ( cx, NEVER_LOOP , span, "this loop never actually loops" , |diag| {
2527 if let Some ( ForLoop {
2628 arg : iterator,
@@ -38,10 +40,15 @@ pub(super) fn check<'tcx>(
3840 Applicability :: Unspecified
3941 } ;
4042
41- diag . span_suggestion_verbose (
43+ let mut suggestions = vec ! [ (
4244 for_span. with_hi( iterator. span. hi( ) ) ,
43- "if you need the first element of the iterator, try writing" ,
4445 for_to_if_let_sugg( cx, iterator, pat) ,
46+ ) ] ;
47+ // Make sure to clear up the diverging sites when we remove a loopp.
48+ suggestions. extend ( break_spans. iter ( ) . map ( |span| ( * span, String :: new ( ) ) ) ) ;
49+ diag. multipart_suggestion_verbose (
50+ "if you need the first element of the iterator, try writing" ,
51+ suggestions,
4552 app,
4653 ) ;
4754 }
@@ -70,22 +77,22 @@ fn contains_any_break_or_continue(block: &Block<'_>) -> bool {
7077/// The first two bits of information are in this enum, and the last part is in the
7178/// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by
7279/// scope.
73- #[ derive( Copy , Clone ) ]
80+ #[ derive( Clone ) ]
7481enum NeverLoopResult {
7582 /// A continue may occur for the main loop.
7683 MayContinueMainLoop ,
7784 /// We have not encountered any main loop continue,
7885 /// but we are diverging (subsequent control flow is not reachable)
79- Diverging ,
86+ Diverging { break_spans : Vec < Span > } ,
8087 /// We have not encountered any main loop continue,
8188 /// and subsequent control flow is (possibly) reachable
8289 Normal ,
8390}
8491
8592#[ must_use]
86- fn absorb_break ( arg : NeverLoopResult ) -> NeverLoopResult {
93+ fn absorb_break ( arg : & NeverLoopResult ) -> NeverLoopResult {
8794 match arg {
88- NeverLoopResult :: Diverging | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
95+ NeverLoopResult :: Diverging { .. } | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
8996 NeverLoopResult :: MayContinueMainLoop => NeverLoopResult :: MayContinueMainLoop ,
9097 }
9198}
@@ -94,7 +101,7 @@ fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
94101#[ must_use]
95102fn combine_seq ( first : NeverLoopResult , second : impl FnOnce ( ) -> NeverLoopResult ) -> NeverLoopResult {
96103 match first {
97- NeverLoopResult :: Diverging | NeverLoopResult :: MayContinueMainLoop => first,
104+ NeverLoopResult :: Diverging { .. } | NeverLoopResult :: MayContinueMainLoop => first,
98105 NeverLoopResult :: Normal => second ( ) ,
99106 }
100107}
@@ -103,7 +110,7 @@ fn combine_seq(first: NeverLoopResult, second: impl FnOnce() -> NeverLoopResult)
103110#[ must_use]
104111fn combine_seq_many ( iter : impl IntoIterator < Item = NeverLoopResult > ) -> NeverLoopResult {
105112 for e in iter {
106- if let NeverLoopResult :: Diverging | NeverLoopResult :: MayContinueMainLoop = e {
113+ if let NeverLoopResult :: Diverging { .. } | NeverLoopResult :: MayContinueMainLoop = e {
107114 return e;
108115 }
109116 }
@@ -118,7 +125,19 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
118125 NeverLoopResult :: MayContinueMainLoop
119126 } ,
120127 ( NeverLoopResult :: Normal , _) | ( _, NeverLoopResult :: Normal ) => NeverLoopResult :: Normal ,
121- ( NeverLoopResult :: Diverging , NeverLoopResult :: Diverging ) => NeverLoopResult :: Diverging ,
128+ (
129+ NeverLoopResult :: Diverging {
130+ break_spans : mut break_spans1,
131+ } ,
132+ NeverLoopResult :: Diverging {
133+ break_spans : mut break_spans2,
134+ } ,
135+ ) => {
136+ break_spans1. append ( & mut break_spans2) ;
137+ NeverLoopResult :: Diverging {
138+ break_spans : break_spans1,
139+ }
140+ } ,
122141 }
123142}
124143
@@ -136,15 +155,15 @@ fn never_loop_block<'tcx>(
136155 combine_seq_many ( iter. map ( |( e, els) | {
137156 let e = never_loop_expr ( cx, e, local_labels, main_loop_id) ;
138157 // els is an else block in a let...else binding
139- els. map_or ( e, |els| {
158+ els. map_or ( e. clone ( ) , |els| {
140159 combine_seq ( e, || match never_loop_block ( cx, els, local_labels, main_loop_id) {
141160 // Returning MayContinueMainLoop here means that
142161 // we will not evaluate the rest of the body
143162 NeverLoopResult :: MayContinueMainLoop => NeverLoopResult :: MayContinueMainLoop ,
144163 // An else block always diverges, so the Normal case should not happen,
145164 // but the analysis is approximate so it might return Normal anyway.
146165 // Returning Normal here says that nothing more happens on the main path
147- NeverLoopResult :: Diverging | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
166+ NeverLoopResult :: Diverging { .. } | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
148167 } )
149168 } )
150169 } ) )
@@ -159,6 +178,45 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
159178 }
160179}
161180
181+ fn stmt_source_span ( stmt : & Stmt < ' _ > ) -> Span {
182+ let call_span = stmt. span . source_callsite ( ) ;
183+ // if it is a macro call, the span will be missing the trailing semicolon
184+ if stmt. span == call_span {
185+ return call_span;
186+ }
187+
188+ // An expression without a trailing semi-colon (must have unit type).
189+ if let StmtKind :: Expr ( ..) = stmt. kind {
190+ return call_span;
191+ }
192+
193+ call_span. with_hi ( call_span. hi ( ) + BytePos ( 1 ) )
194+ }
195+
196+ /// Returns a Vec of all the individual spans after the highlighted expression in a block
197+ fn all_spans_after_expr ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Vec < Span > {
198+ if let Node :: Stmt ( stmt) = cx. tcx . parent_hir_node ( expr. hir_id ) {
199+ if let Node :: Block ( block) = cx. tcx . parent_hir_node ( stmt. hir_id ) {
200+ return block
201+ . stmts
202+ . iter ( )
203+ . skip_while ( |inner| inner. hir_id != stmt. hir_id )
204+ . map ( stmt_source_span)
205+ . chain ( if let Some ( e) = block. expr { vec ! [ e. span] } else { vec ! [ ] } )
206+ . collect ( ) ;
207+ }
208+
209+ return vec ! [ stmt. span] ;
210+ }
211+
212+ vec ! [ ]
213+ }
214+
215+ fn is_label_for_block ( cx : & LateContext < ' _ > , dest : & Destination ) -> bool {
216+ dest. target_id
217+ . is_ok_and ( |hir_id| matches ! ( cx. tcx. hir_node( hir_id) , Node :: Block ( _) ) )
218+ }
219+
162220#[ allow( clippy:: too_many_lines) ]
163221fn never_loop_expr < ' tcx > (
164222 cx : & LateContext < ' tcx > ,
@@ -197,7 +255,7 @@ fn never_loop_expr<'tcx>(
197255 ExprKind :: Loop ( b, _, _, _) => {
198256 // We don't attempt to track reachability after a loop,
199257 // just assume there may have been a break somewhere
200- absorb_break ( never_loop_block ( cx, b, local_labels, main_loop_id) )
258+ absorb_break ( & never_loop_block ( cx, b, local_labels, main_loop_id) )
201259 } ,
202260 ExprKind :: If ( e, e2, e3) => {
203261 let e1 = never_loop_expr ( cx, e, local_labels, main_loop_id) ;
@@ -212,9 +270,10 @@ fn never_loop_expr<'tcx>(
212270 ExprKind :: Match ( e, arms, _) => {
213271 let e = never_loop_expr ( cx, e, local_labels, main_loop_id) ;
214272 combine_seq ( e, || {
215- arms. iter ( ) . fold ( NeverLoopResult :: Diverging , |a, b| {
216- combine_branches ( a, never_loop_expr ( cx, b. body , local_labels, main_loop_id) )
217- } )
273+ arms. iter ( )
274+ . fold ( NeverLoopResult :: Diverging { break_spans : vec ! [ ] } , |a, b| {
275+ combine_branches ( a, never_loop_expr ( cx, b. body , local_labels, main_loop_id) )
276+ } )
218277 } )
219278 } ,
220279 ExprKind :: Block ( b, _) => {
@@ -224,7 +283,7 @@ fn never_loop_expr<'tcx>(
224283 let ret = never_loop_block ( cx, b, local_labels, main_loop_id) ;
225284 let jumped_to = b. targeted_by_break && local_labels. pop ( ) . unwrap ( ) . 1 ;
226285 match ret {
227- NeverLoopResult :: Diverging if jumped_to => NeverLoopResult :: Normal ,
286+ NeverLoopResult :: Diverging { .. } if jumped_to => NeverLoopResult :: Normal ,
228287 _ => ret,
229288 }
230289 } ,
@@ -235,25 +294,39 @@ fn never_loop_expr<'tcx>(
235294 if id == main_loop_id {
236295 NeverLoopResult :: MayContinueMainLoop
237296 } else {
238- NeverLoopResult :: Diverging
297+ NeverLoopResult :: Diverging {
298+ break_spans : all_spans_after_expr ( cx, expr) ,
299+ }
239300 }
240301 } ,
241- ExprKind :: Break ( _ , e ) | ExprKind :: Ret ( e) => {
302+ ExprKind :: Ret ( e) => {
242303 let first = e. as_ref ( ) . map_or ( NeverLoopResult :: Normal , |e| {
243304 never_loop_expr ( cx, e, local_labels, main_loop_id)
244305 } ) ;
245306 combine_seq ( first, || {
246307 // checks if break targets a block instead of a loop
247- if let ExprKind :: Break ( Destination { target_id : Ok ( t) , .. } , _) = expr. kind
248- && let Some ( ( _, reachable) ) = local_labels. iter_mut ( ) . find ( |( label, _) | * label == t)
249- {
250- * reachable = true ;
308+ mark_block_as_reachable ( expr, local_labels) ;
309+ NeverLoopResult :: Diverging { break_spans : vec ! [ ] }
310+ } )
311+ } ,
312+ ExprKind :: Break ( dest, e) => {
313+ let first = e. as_ref ( ) . map_or ( NeverLoopResult :: Normal , |e| {
314+ never_loop_expr ( cx, e, local_labels, main_loop_id)
315+ } ) ;
316+ combine_seq ( first, || {
317+ // checks if break targets a block instead of a loop
318+ mark_block_as_reachable ( expr, local_labels) ;
319+ NeverLoopResult :: Diverging {
320+ break_spans : if is_label_for_block ( cx, & dest) {
321+ vec ! [ ]
322+ } else {
323+ all_spans_after_expr ( cx, expr)
324+ } ,
251325 }
252- NeverLoopResult :: Diverging
253326 } )
254327 } ,
255328 ExprKind :: Become ( e) => combine_seq ( never_loop_expr ( cx, e, local_labels, main_loop_id) , || {
256- NeverLoopResult :: Diverging
329+ NeverLoopResult :: Diverging { break_spans : vec ! [ ] }
257330 } ) ,
258331 ExprKind :: InlineAsm ( asm) => combine_seq_many ( asm. operands . iter ( ) . map ( |( o, _) | match o {
259332 InlineAsmOperand :: In { expr, .. } | InlineAsmOperand :: InOut { expr, .. } => {
@@ -283,12 +356,12 @@ fn never_loop_expr<'tcx>(
283356 } ;
284357 let result = combine_seq ( result, || {
285358 if cx. typeck_results ( ) . expr_ty ( expr) . is_never ( ) {
286- NeverLoopResult :: Diverging
359+ NeverLoopResult :: Diverging { break_spans : vec ! [ ] }
287360 } else {
288361 NeverLoopResult :: Normal
289362 }
290363 } ) ;
291- if let NeverLoopResult :: Diverging = result
364+ if let NeverLoopResult :: Diverging { .. } = result
292365 && let Some ( macro_call) = root_macro_call_first_node ( cx, expr)
293366 && let Some ( sym:: todo_macro) = cx. tcx . get_diagnostic_name ( macro_call. def_id )
294367 {
@@ -316,3 +389,11 @@ fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>)
316389
317390 format ! ( "if let Some({pat_snippet}) = {iter_snippet}.next()" )
318391}
392+
393+ fn mark_block_as_reachable ( expr : & Expr < ' _ > , local_labels : & mut [ ( HirId , bool ) ] ) {
394+ if let ExprKind :: Break ( Destination { target_id : Ok ( t) , .. } , _) = expr. kind
395+ && let Some ( ( _, reachable) ) = local_labels. iter_mut ( ) . find ( |( label, _) | * label == t)
396+ {
397+ * reachable = true ;
398+ }
399+ }
0 commit comments