1
1
use super :: utils:: make_iterator_snippet;
2
2
use super :: NEVER_LOOP ;
3
- use clippy_utils:: diagnostics :: span_lint_and_then ;
3
+ use clippy_utils:: consts :: constant ;
4
4
use clippy_utils:: higher:: ForLoop ;
5
5
use clippy_utils:: source:: snippet;
6
+ use clippy_utils:: { consts:: Constant , diagnostics:: span_lint_and_then} ;
6
7
use rustc_errors:: Applicability ;
7
8
use rustc_hir:: { Block , Destination , Expr , ExprKind , HirId , InlineAsmOperand , Pat , Stmt , StmtKind } ;
8
9
use rustc_lint:: LateContext ;
9
10
use rustc_span:: Span ;
10
11
use std:: iter:: { once, Iterator } ;
11
12
12
- pub ( super ) fn check (
13
- cx : & LateContext < ' _ > ,
14
- block : & Block < ' _ > ,
13
+ pub ( super ) fn check < ' tcx > (
14
+ cx : & LateContext < ' tcx > ,
15
+ block : & Block < ' tcx > ,
15
16
loop_id : HirId ,
16
17
span : Span ,
17
18
for_loop : Option < & ForLoop < ' _ > > ,
18
19
) {
19
- match never_loop_block ( block, & mut Vec :: new ( ) , loop_id) {
20
+ match never_loop_block ( cx , block, & mut Vec :: new ( ) , loop_id) {
20
21
NeverLoopResult :: AlwaysBreak => {
21
22
span_lint_and_then ( cx, NEVER_LOOP , span, "this loop never actually loops" , |diag| {
22
23
if let Some ( ForLoop {
@@ -95,18 +96,23 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirI
95
96
}
96
97
}
97
98
98
- fn never_loop_block ( block : & Block < ' _ > , ignore_ids : & mut Vec < HirId > , main_loop_id : HirId ) -> NeverLoopResult {
99
+ fn never_loop_block < ' tcx > (
100
+ cx : & LateContext < ' tcx > ,
101
+ block : & Block < ' tcx > ,
102
+ ignore_ids : & mut Vec < HirId > ,
103
+ main_loop_id : HirId ,
104
+ ) -> NeverLoopResult {
99
105
let iter = block
100
106
. stmts
101
107
. iter ( )
102
108
. filter_map ( stmt_to_expr)
103
109
. chain ( block. expr . map ( |expr| ( expr, None ) ) ) ;
104
110
105
111
iter. map ( |( e, els) | {
106
- let e = never_loop_expr ( e, ignore_ids, main_loop_id) ;
112
+ let e = never_loop_expr ( cx , e, ignore_ids, main_loop_id) ;
107
113
// els is an else block in a let...else binding
108
114
els. map_or ( e, |els| {
109
- combine_branches ( e, never_loop_block ( els, ignore_ids, main_loop_id) , ignore_ids)
115
+ combine_branches ( e, never_loop_block ( cx , els, ignore_ids, main_loop_id) , ignore_ids)
110
116
} )
111
117
} )
112
118
. fold ( NeverLoopResult :: Otherwise , combine_seq)
@@ -122,61 +128,72 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
122
128
}
123
129
124
130
#[ allow( clippy:: too_many_lines) ]
125
- fn never_loop_expr ( expr : & Expr < ' _ > , ignore_ids : & mut Vec < HirId > , main_loop_id : HirId ) -> NeverLoopResult {
131
+ fn never_loop_expr < ' tcx > (
132
+ cx : & LateContext < ' tcx > ,
133
+ expr : & Expr < ' tcx > ,
134
+ ignore_ids : & mut Vec < HirId > ,
135
+ main_loop_id : HirId ,
136
+ ) -> NeverLoopResult {
126
137
match expr. kind {
127
138
ExprKind :: Unary ( _, e)
128
139
| ExprKind :: Cast ( e, _)
129
140
| ExprKind :: Type ( e, _)
130
141
| ExprKind :: Field ( e, _)
131
142
| ExprKind :: AddrOf ( _, _, e)
132
143
| ExprKind :: Repeat ( e, _)
133
- | ExprKind :: DropTemps ( e) => never_loop_expr ( e, ignore_ids, main_loop_id) ,
134
- ExprKind :: Let ( let_expr) => never_loop_expr ( let_expr. init , ignore_ids, main_loop_id) ,
135
- ExprKind :: Array ( es) | ExprKind :: Tup ( es) => never_loop_expr_all ( & mut es. iter ( ) , ignore_ids, main_loop_id) ,
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) ,
136
147
ExprKind :: MethodCall ( _, receiver, es, _) => never_loop_expr_all (
148
+ cx,
137
149
& mut std:: iter:: once ( receiver) . chain ( es. iter ( ) ) ,
138
150
ignore_ids,
139
151
main_loop_id,
140
152
) ,
141
153
ExprKind :: Struct ( _, fields, base) => {
142
- let fields = never_loop_expr_all ( & mut fields. iter ( ) . map ( |f| f. expr ) , ignore_ids, main_loop_id) ;
154
+ let fields = never_loop_expr_all ( cx , & mut fields. iter ( ) . map ( |f| f. expr ) , ignore_ids, main_loop_id) ;
143
155
if let Some ( base) = base {
144
- combine_seq ( fields, never_loop_expr ( base, ignore_ids, main_loop_id) )
156
+ combine_seq ( fields, never_loop_expr ( cx , base, ignore_ids, main_loop_id) )
145
157
} else {
146
158
fields
147
159
}
148
160
} ,
149
- ExprKind :: Call ( e, es) => never_loop_expr_all ( & mut once ( e) . chain ( es. iter ( ) ) , ignore_ids, main_loop_id) ,
161
+ ExprKind :: Call ( e, es) => never_loop_expr_all ( cx , & mut once ( e) . chain ( es. iter ( ) ) , ignore_ids, main_loop_id) ,
150
162
ExprKind :: Binary ( _, e1, e2)
151
163
| ExprKind :: Assign ( e1, e2, _)
152
164
| ExprKind :: AssignOp ( _, e1, e2)
153
- | ExprKind :: Index ( e1, e2) => never_loop_expr_all ( & mut [ e1, e2] . iter ( ) . copied ( ) , ignore_ids, main_loop_id) ,
165
+ | ExprKind :: Index ( e1, e2) => never_loop_expr_all ( cx , & mut [ e1, e2] . iter ( ) . copied ( ) , ignore_ids, main_loop_id) ,
154
166
ExprKind :: Loop ( b, _, _, _) => {
155
167
// Break can come from the inner loop so remove them.
156
- absorb_break ( never_loop_block ( b, ignore_ids, main_loop_id) )
168
+ absorb_break ( never_loop_block ( cx , b, ignore_ids, main_loop_id) )
157
169
} ,
158
170
ExprKind :: If ( e, e2, e3) => {
159
- let e1 = never_loop_expr ( e, ignore_ids, main_loop_id) ;
160
- let e2 = never_loop_expr ( e2, ignore_ids, main_loop_id) ;
171
+ let e1 = never_loop_expr ( cx, e, ignore_ids, main_loop_id) ;
172
+ let e2 = never_loop_expr ( cx, e2, ignore_ids, main_loop_id) ;
173
+ // If we know the `if` condition evaluates to `true`, don't check everything past it; it
174
+ // should just return whatever's evaluated for `e1` and `e2` since `e3` is unreachable
175
+ if let Some ( Constant :: Bool ( true ) ) = constant ( cx, cx. typeck_results ( ) , e) {
176
+ return combine_seq ( e1, e2) ;
177
+ }
161
178
let e3 = e3. as_ref ( ) . map_or ( NeverLoopResult :: Otherwise , |e| {
162
- never_loop_expr ( e, ignore_ids, main_loop_id)
179
+ never_loop_expr ( cx , e, ignore_ids, main_loop_id)
163
180
} ) ;
164
181
combine_seq ( e1, combine_branches ( e2, e3, ignore_ids) )
165
182
} ,
166
183
ExprKind :: Match ( e, arms, _) => {
167
- let e = never_loop_expr ( e, ignore_ids, main_loop_id) ;
184
+ let e = never_loop_expr ( cx , e, ignore_ids, main_loop_id) ;
168
185
if arms. is_empty ( ) {
169
186
e
170
187
} else {
171
- let arms = never_loop_expr_branch ( & mut arms. iter ( ) . map ( |a| a. body ) , ignore_ids, main_loop_id) ;
188
+ let arms = never_loop_expr_branch ( cx , & mut arms. iter ( ) . map ( |a| a. body ) , ignore_ids, main_loop_id) ;
172
189
combine_seq ( e, arms)
173
190
}
174
191
} ,
175
192
ExprKind :: Block ( b, l) => {
176
193
if l. is_some ( ) {
177
194
ignore_ids. push ( b. hir_id ) ;
178
195
}
179
- let ret = never_loop_block ( b, ignore_ids, main_loop_id) ;
196
+ let ret = never_loop_block ( cx , b, ignore_ids, main_loop_id) ;
180
197
if l. is_some ( ) {
181
198
ignore_ids. pop ( ) ;
182
199
}
@@ -198,11 +215,11 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
198
215
// checks if break targets a block instead of a loop
199
216
ExprKind :: Break ( Destination { target_id : Ok ( t) , .. } , e) if ignore_ids. contains ( & t) => e
200
217
. map_or ( NeverLoopResult :: IgnoreUntilEnd ( t) , |e| {
201
- never_loop_expr ( e, ignore_ids, main_loop_id)
218
+ never_loop_expr ( cx , e, ignore_ids, main_loop_id)
202
219
} ) ,
203
220
ExprKind :: Break ( _, e) | ExprKind :: Ret ( e) => e. as_ref ( ) . map_or ( NeverLoopResult :: AlwaysBreak , |e| {
204
221
combine_seq (
205
- never_loop_expr ( e, ignore_ids, main_loop_id) ,
222
+ never_loop_expr ( cx , e, ignore_ids, main_loop_id) ,
206
223
NeverLoopResult :: AlwaysBreak ,
207
224
)
208
225
} ) ,
@@ -211,12 +228,13 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
211
228
. iter ( )
212
229
. map ( |( o, _) | match o {
213
230
InlineAsmOperand :: In { expr, .. } | InlineAsmOperand :: InOut { expr, .. } => {
214
- never_loop_expr ( expr, ignore_ids, main_loop_id)
231
+ never_loop_expr ( cx , expr, ignore_ids, main_loop_id)
215
232
} ,
216
233
InlineAsmOperand :: Out { expr, .. } => {
217
- never_loop_expr_all ( & mut expr. iter ( ) . copied ( ) , ignore_ids, main_loop_id)
234
+ never_loop_expr_all ( cx , & mut expr. iter ( ) . copied ( ) , ignore_ids, main_loop_id)
218
235
} ,
219
236
InlineAsmOperand :: SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all (
237
+ cx,
220
238
& mut once ( * in_expr) . chain ( out_expr. iter ( ) . copied ( ) ) ,
221
239
ignore_ids,
222
240
main_loop_id,
@@ -236,22 +254,24 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
236
254
}
237
255
}
238
256
239
- fn never_loop_expr_all < ' a , T : Iterator < Item = & ' a Expr < ' a > > > (
257
+ fn never_loop_expr_all < ' tcx , T : Iterator < Item = & ' tcx Expr < ' tcx > > > (
258
+ cx : & LateContext < ' tcx > ,
240
259
es : & mut T ,
241
260
ignore_ids : & mut Vec < HirId > ,
242
261
main_loop_id : HirId ,
243
262
) -> NeverLoopResult {
244
- es. map ( |e| never_loop_expr ( e, ignore_ids, main_loop_id) )
263
+ es. map ( |e| never_loop_expr ( cx , e, ignore_ids, main_loop_id) )
245
264
. fold ( NeverLoopResult :: Otherwise , combine_seq)
246
265
}
247
266
248
- fn never_loop_expr_branch < ' a , T : Iterator < Item = & ' a Expr < ' a > > > (
267
+ fn never_loop_expr_branch < ' tcx , T : Iterator < Item = & ' tcx Expr < ' tcx > > > (
268
+ cx : & LateContext < ' tcx > ,
249
269
e : & mut T ,
250
270
ignore_ids : & mut Vec < HirId > ,
251
271
main_loop_id : HirId ,
252
272
) -> NeverLoopResult {
253
273
e. fold ( NeverLoopResult :: AlwaysBreak , |a, b| {
254
- combine_branches ( a, never_loop_expr ( b, ignore_ids, main_loop_id) , ignore_ids)
274
+ combine_branches ( a, never_loop_expr ( cx , b, ignore_ids, main_loop_id) , ignore_ids)
255
275
} )
256
276
}
257
277
0 commit comments