@@ -6,9 +6,11 @@ use clippy_utils::macros::root_macro_call_first_node;
6
6
use clippy_utils:: source:: snippet;
7
7
use clippy_utils:: visitors:: { Descend , for_each_expr_without_closures} ;
8
8
use 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
+ } ;
10
12
use rustc_lint:: LateContext ;
11
- use rustc_span:: { Span , sym} ;
13
+ use rustc_span:: { BytePos , Span , sym} ;
12
14
use std:: iter:: once;
13
15
use std:: ops:: ControlFlow ;
14
16
@@ -20,7 +22,7 @@ pub(super) fn check<'tcx>(
20
22
for_loop : Option < & ForLoop < ' _ > > ,
21
23
) {
22
24
match never_loop_block ( cx, block, & mut Vec :: new ( ) , loop_id) {
23
- NeverLoopResult :: Diverging => {
25
+ NeverLoopResult :: Diverging ( ref inner ) => {
24
26
span_lint_and_then ( cx, NEVER_LOOP , span, "this loop never actually loops" , |diag| {
25
27
if let Some ( ForLoop {
26
28
arg : iterator,
@@ -38,10 +40,14 @@ pub(super) fn check<'tcx>(
38
40
Applicability :: Unspecified
39
41
} ;
40
42
41
- diag . span_suggestion_verbose (
43
+ let mut suggestions = vec ! [ (
42
44
for_span. with_hi( iterator. span. hi( ) ) ,
43
- "if you need the first element of the iterator, try writing" ,
44
45
for_to_if_let_sugg( cx, iterator, pat) ,
46
+ ) ] ;
47
+ suggestions. extend ( inner. iter ( ) . map ( |span| ( * span, String :: new ( ) ) ) ) ;
48
+ diag. multipart_suggestion_verbose (
49
+ "if you need the first element of the iterator, try writing" ,
50
+ suggestions,
45
51
app,
46
52
) ;
47
53
}
@@ -70,22 +76,22 @@ fn contains_any_break_or_continue(block: &Block<'_>) -> bool {
70
76
/// The first two bits of information are in this enum, and the last part is in the
71
77
/// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by
72
78
/// scope.
73
- #[ derive( Copy , Clone ) ]
79
+ #[ derive( Clone ) ]
74
80
enum NeverLoopResult {
75
81
/// A continue may occur for the main loop.
76
82
MayContinueMainLoop ,
77
83
/// We have not encountered any main loop continue,
78
84
/// but we are diverging (subsequent control flow is not reachable)
79
- Diverging ,
85
+ Diverging ( Vec < Span > ) ,
80
86
/// We have not encountered any main loop continue,
81
87
/// and subsequent control flow is (possibly) reachable
82
88
Normal ,
83
89
}
84
90
85
91
#[ must_use]
86
- fn absorb_break ( arg : NeverLoopResult ) -> NeverLoopResult {
92
+ fn absorb_break ( arg : & NeverLoopResult ) -> NeverLoopResult {
87
93
match arg {
88
- NeverLoopResult :: Diverging | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
94
+ NeverLoopResult :: Diverging ( .. ) | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
89
95
NeverLoopResult :: MayContinueMainLoop => NeverLoopResult :: MayContinueMainLoop ,
90
96
}
91
97
}
@@ -94,7 +100,7 @@ fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
94
100
#[ must_use]
95
101
fn combine_seq ( first : NeverLoopResult , second : impl FnOnce ( ) -> NeverLoopResult ) -> NeverLoopResult {
96
102
match first {
97
- NeverLoopResult :: Diverging | NeverLoopResult :: MayContinueMainLoop => first,
103
+ NeverLoopResult :: Diverging ( .. ) | NeverLoopResult :: MayContinueMainLoop => first,
98
104
NeverLoopResult :: Normal => second ( ) ,
99
105
}
100
106
}
@@ -103,7 +109,7 @@ fn combine_seq(first: NeverLoopResult, second: impl FnOnce() -> NeverLoopResult)
103
109
#[ must_use]
104
110
fn combine_seq_many ( iter : impl IntoIterator < Item = NeverLoopResult > ) -> NeverLoopResult {
105
111
for e in iter {
106
- if let NeverLoopResult :: Diverging | NeverLoopResult :: MayContinueMainLoop = e {
112
+ if let NeverLoopResult :: Diverging ( .. ) | NeverLoopResult :: MayContinueMainLoop = e {
107
113
return e;
108
114
}
109
115
}
@@ -118,7 +124,10 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
118
124
NeverLoopResult :: MayContinueMainLoop
119
125
} ,
120
126
( NeverLoopResult :: Normal , _) | ( _, NeverLoopResult :: Normal ) => NeverLoopResult :: Normal ,
121
- ( NeverLoopResult :: Diverging , NeverLoopResult :: Diverging ) => NeverLoopResult :: Diverging ,
127
+ ( NeverLoopResult :: Diverging ( mut inner1) , NeverLoopResult :: Diverging ( mut inner2) ) => {
128
+ inner1. append ( & mut inner2) ;
129
+ NeverLoopResult :: Diverging ( inner1)
130
+ } ,
122
131
}
123
132
}
124
133
@@ -136,15 +145,15 @@ fn never_loop_block<'tcx>(
136
145
combine_seq_many ( iter. map ( |( e, els) | {
137
146
let e = never_loop_expr ( cx, e, local_labels, main_loop_id) ;
138
147
// els is an else block in a let...else binding
139
- els. map_or ( e, |els| {
148
+ els. map_or ( e. clone ( ) , |els| {
140
149
combine_seq ( e, || match never_loop_block ( cx, els, local_labels, main_loop_id) {
141
150
// Returning MayContinueMainLoop here means that
142
151
// we will not evaluate the rest of the body
143
152
NeverLoopResult :: MayContinueMainLoop => NeverLoopResult :: MayContinueMainLoop ,
144
153
// An else block always diverges, so the Normal case should not happen,
145
154
// but the analysis is approximate so it might return Normal anyway.
146
155
// Returning Normal here says that nothing more happens on the main path
147
- NeverLoopResult :: Diverging | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
156
+ NeverLoopResult :: Diverging ( .. ) | NeverLoopResult :: Normal => NeverLoopResult :: Normal ,
148
157
} )
149
158
} )
150
159
} ) )
@@ -159,6 +168,38 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
159
168
}
160
169
}
161
170
171
+ fn stmt_source_span ( stmt : & Stmt < ' _ > ) -> Span {
172
+ let call_span = stmt. span . source_callsite ( ) ;
173
+ // if it is a macro call, the span will missing the trailing semicolon
174
+ if stmt. span == call_span {
175
+ return call_span;
176
+ }
177
+ call_span. with_hi ( call_span. hi ( ) + BytePos ( 1 ) )
178
+ }
179
+
180
+ fn expr_find_remove_span ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Vec < Span > {
181
+ if let Node :: Stmt ( stmt) = cx. tcx . parent_hir_node ( expr. hir_id ) {
182
+ if let Node :: Block ( block) = cx. tcx . parent_hir_node ( stmt. hir_id ) {
183
+ return block
184
+ . stmts
185
+ . iter ( )
186
+ . skip_while ( |inner| inner. hir_id != stmt. hir_id )
187
+ . map ( stmt_source_span)
188
+ . chain ( if let Some ( e) = block. expr { vec ! [ e. span] } else { vec ! [ ] } )
189
+ . collect ( ) ;
190
+ }
191
+
192
+ return vec ! [ stmt. span] ;
193
+ }
194
+
195
+ vec ! [ ]
196
+ }
197
+
198
+ fn is_label_for_block ( cx : & LateContext < ' _ > , dest : & Destination ) -> bool {
199
+ dest. target_id
200
+ . is_ok_and ( |hir_id| matches ! ( cx. tcx. hir_node( hir_id) , Node :: Block ( _) ) )
201
+ }
202
+
162
203
#[ allow( clippy:: too_many_lines) ]
163
204
fn never_loop_expr < ' tcx > (
164
205
cx : & LateContext < ' tcx > ,
@@ -197,7 +238,7 @@ fn never_loop_expr<'tcx>(
197
238
ExprKind :: Loop ( b, _, _, _) => {
198
239
// We don't attempt to track reachability after a loop,
199
240
// just assume there may have been a break somewhere
200
- absorb_break ( never_loop_block ( cx, b, local_labels, main_loop_id) )
241
+ absorb_break ( & never_loop_block ( cx, b, local_labels, main_loop_id) )
201
242
} ,
202
243
ExprKind :: If ( e, e2, e3) => {
203
244
let e1 = never_loop_expr ( cx, e, local_labels, main_loop_id) ;
@@ -212,7 +253,7 @@ fn never_loop_expr<'tcx>(
212
253
ExprKind :: Match ( e, arms, _) => {
213
254
let e = never_loop_expr ( cx, e, local_labels, main_loop_id) ;
214
255
combine_seq ( e, || {
215
- arms. iter ( ) . fold ( NeverLoopResult :: Diverging , |a, b| {
256
+ arms. iter ( ) . fold ( NeverLoopResult :: Diverging ( vec ! [ ] ) , |a, b| {
216
257
combine_branches ( a, never_loop_expr ( cx, b. body , local_labels, main_loop_id) )
217
258
} )
218
259
} )
@@ -224,7 +265,7 @@ fn never_loop_expr<'tcx>(
224
265
let ret = never_loop_block ( cx, b, local_labels, main_loop_id) ;
225
266
let jumped_to = b. targeted_by_break && local_labels. pop ( ) . unwrap ( ) . 1 ;
226
267
match ret {
227
- NeverLoopResult :: Diverging if jumped_to => NeverLoopResult :: Normal ,
268
+ NeverLoopResult :: Diverging ( .. ) if jumped_to => NeverLoopResult :: Normal ,
228
269
_ => ret,
229
270
}
230
271
} ,
@@ -235,10 +276,10 @@ fn never_loop_expr<'tcx>(
235
276
if id == main_loop_id {
236
277
NeverLoopResult :: MayContinueMainLoop
237
278
} else {
238
- NeverLoopResult :: Diverging
279
+ NeverLoopResult :: Diverging ( expr_find_remove_span ( cx , expr ) )
239
280
}
240
281
} ,
241
- ExprKind :: Break ( _ , e ) | ExprKind :: Ret ( e) => {
282
+ ExprKind :: Ret ( e) => {
242
283
let first = e. as_ref ( ) . map_or ( NeverLoopResult :: Normal , |e| {
243
284
never_loop_expr ( cx, e, local_labels, main_loop_id)
244
285
} ) ;
@@ -249,11 +290,30 @@ fn never_loop_expr<'tcx>(
249
290
{
250
291
* reachable = true ;
251
292
}
252
- NeverLoopResult :: Diverging
293
+ NeverLoopResult :: Diverging ( vec ! [ ] )
294
+ } )
295
+ } ,
296
+ ExprKind :: Break ( dest, e) => {
297
+ let first = e. as_ref ( ) . map_or ( NeverLoopResult :: Normal , |e| {
298
+ never_loop_expr ( cx, e, local_labels, main_loop_id)
299
+ } ) ;
300
+ combine_seq ( first, || {
301
+ // checks if break targets a block instead of a loop
302
+ if let ExprKind :: Break ( Destination { target_id : Ok ( t) , .. } , _) = expr. kind
303
+ && let Some ( ( _, reachable) ) = local_labels. iter_mut ( ) . find ( |( label, _) | * label == t)
304
+ {
305
+ * reachable = true ;
306
+ }
307
+
308
+ NeverLoopResult :: Diverging ( if is_label_for_block ( cx, & dest) {
309
+ vec ! [ ]
310
+ } else {
311
+ expr_find_remove_span ( cx, expr)
312
+ } )
253
313
} )
254
314
} ,
255
315
ExprKind :: Become ( e) => combine_seq ( never_loop_expr ( cx, e, local_labels, main_loop_id) , || {
256
- NeverLoopResult :: Diverging
316
+ NeverLoopResult :: Diverging ( vec ! [ ] )
257
317
} ) ,
258
318
ExprKind :: InlineAsm ( asm) => combine_seq_many ( asm. operands . iter ( ) . map ( |( o, _) | match o {
259
319
InlineAsmOperand :: In { expr, .. } | InlineAsmOperand :: InOut { expr, .. } => {
@@ -283,12 +343,12 @@ fn never_loop_expr<'tcx>(
283
343
} ;
284
344
let result = combine_seq ( result, || {
285
345
if cx. typeck_results ( ) . expr_ty ( expr) . is_never ( ) {
286
- NeverLoopResult :: Diverging
346
+ NeverLoopResult :: Diverging ( vec ! [ ] )
287
347
} else {
288
348
NeverLoopResult :: Normal
289
349
}
290
350
} ) ;
291
- if let NeverLoopResult :: Diverging = result
351
+ if let NeverLoopResult :: Diverging ( .. ) = result
292
352
&& let Some ( macro_call) = root_macro_call_first_node ( cx, expr)
293
353
&& let Some ( sym:: todo_macro) = cx. tcx . get_diagnostic_name ( macro_call. def_id )
294
354
{
0 commit comments