@@ -5,18 +5,15 @@ use clippy_utils::diagnostics::span_lint_and_then;
5
5
use clippy_utils:: higher:: IfLetOrMatch ;
6
6
use clippy_utils:: source:: snippet_with_context;
7
7
use clippy_utils:: ty:: is_type_diagnostic_item;
8
- use clippy_utils:: visitors:: { Descend , Visitable } ;
9
- use clippy_utils:: { is_lint_allowed, pat_and_expr_can_be_question_mark, peel_blocks} ;
8
+ use clippy_utils:: { is_lint_allowed, is_never_expr, pat_and_expr_can_be_question_mark, peel_blocks} ;
10
9
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
11
10
use rustc_errors:: Applicability ;
12
- use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
13
- use rustc_hir:: { Expr , ExprKind , HirId , ItemId , Local , MatchSource , Pat , PatKind , QPath , Stmt , StmtKind , Ty } ;
11
+ use rustc_hir:: { Expr , ExprKind , MatchSource , Pat , PatKind , QPath , Stmt , StmtKind } ;
14
12
use rustc_lint:: { LateContext , LintContext } ;
15
13
use rustc_middle:: lint:: in_external_macro;
16
14
use rustc_session:: declare_tool_lint;
17
15
use rustc_span:: symbol:: { sym, Symbol } ;
18
16
use rustc_span:: Span ;
19
- use std:: ops:: ControlFlow ;
20
17
use std:: slice;
21
18
22
19
declare_clippy_lint ! {
@@ -51,7 +48,7 @@ declare_clippy_lint! {
51
48
}
52
49
53
50
impl < ' tcx > QuestionMark {
54
- pub ( crate ) fn check_manual_let_else ( & mut self , cx : & LateContext < ' _ > , stmt : & ' tcx Stmt < ' tcx > ) {
51
+ pub ( crate ) fn check_manual_let_else ( & mut self , cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < ' tcx > ) {
55
52
if !self . msrv . meets ( msrvs:: LET_ELSE ) || in_external_macro ( cx. sess ( ) , stmt. span ) {
56
53
return ;
57
54
}
@@ -67,7 +64,7 @@ impl<'tcx> QuestionMark {
67
64
IfLetOrMatch :: IfLet ( if_let_expr, let_pat, if_then, if_else) => {
68
65
if let Some ( ident_map) = expr_simple_identity_map ( local. pat , let_pat, if_then)
69
66
&& let Some ( if_else) = if_else
70
- && expr_diverges ( cx, if_else)
67
+ && is_never_expr ( cx, if_else)
71
68
&& let qm_allowed = is_lint_allowed ( cx, QUESTION_MARK , stmt. hir_id )
72
69
&& ( qm_allowed || pat_and_expr_can_be_question_mark ( cx, let_pat, if_else) . is_none ( ) )
73
70
{
@@ -94,7 +91,7 @@ impl<'tcx> QuestionMark {
94
91
let diverging_arm_opt = arms
95
92
. iter ( )
96
93
. enumerate ( )
97
- . find ( |( _, arm) | expr_diverges ( cx, arm. body ) && pat_allowed_for_else ( cx, arm. pat , check_types) ) ;
94
+ . find ( |( _, arm) | is_never_expr ( cx, arm. body ) && pat_allowed_for_else ( cx, arm. pat , check_types) ) ;
98
95
let Some ( ( idx, diverging_arm) ) = diverging_arm_opt else {
99
96
return ;
100
97
} ;
@@ -272,104 +269,6 @@ fn replace_in_pattern(
272
269
sn_pat. into_owned ( )
273
270
}
274
271
275
- /// Check whether an expression is divergent. May give false negatives.
276
- fn expr_diverges ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
277
- struct V < ' cx , ' tcx > {
278
- cx : & ' cx LateContext < ' tcx > ,
279
- res : ControlFlow < ( ) , Descend > ,
280
- }
281
- impl < ' tcx > Visitor < ' tcx > for V < ' _ , ' _ > {
282
- fn visit_expr ( & mut self , e : & ' tcx Expr < ' tcx > ) {
283
- fn is_never ( cx : & LateContext < ' _ > , expr : & ' _ Expr < ' _ > ) -> bool {
284
- if let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( expr) {
285
- return ty. is_never ( ) ;
286
- }
287
- false
288
- }
289
-
290
- if self . res . is_break ( ) {
291
- return ;
292
- }
293
-
294
- // We can't just call is_never on expr and be done, because the type system
295
- // sometimes coerces the ! type to something different before we can get
296
- // our hands on it. So instead, we do a manual search. We do fall back to
297
- // is_never in some places when there is no better alternative.
298
- self . res = match e. kind {
299
- ExprKind :: Continue ( _) | ExprKind :: Break ( _, _) | ExprKind :: Ret ( _) => ControlFlow :: Break ( ( ) ) ,
300
- ExprKind :: Call ( call, _) => {
301
- if is_never ( self . cx , e) || is_never ( self . cx , call) {
302
- ControlFlow :: Break ( ( ) )
303
- } else {
304
- ControlFlow :: Continue ( Descend :: Yes )
305
- }
306
- } ,
307
- ExprKind :: MethodCall ( ..) => {
308
- if is_never ( self . cx , e) {
309
- ControlFlow :: Break ( ( ) )
310
- } else {
311
- ControlFlow :: Continue ( Descend :: Yes )
312
- }
313
- } ,
314
- ExprKind :: If ( if_expr, if_then, if_else) => {
315
- let else_diverges = if_else. map_or ( false , |ex| expr_diverges ( self . cx , ex) ) ;
316
- let diverges =
317
- expr_diverges ( self . cx , if_expr) || ( else_diverges && expr_diverges ( self . cx , if_then) ) ;
318
- if diverges {
319
- ControlFlow :: Break ( ( ) )
320
- } else {
321
- ControlFlow :: Continue ( Descend :: No )
322
- }
323
- } ,
324
- ExprKind :: Match ( match_expr, match_arms, _) => {
325
- let diverges = expr_diverges ( self . cx , match_expr)
326
- || match_arms. iter ( ) . all ( |arm| {
327
- let guard_diverges = arm. guard . as_ref ( ) . map_or ( false , |g| expr_diverges ( self . cx , g. body ( ) ) ) ;
328
- guard_diverges || expr_diverges ( self . cx , arm. body )
329
- } ) ;
330
- if diverges {
331
- ControlFlow :: Break ( ( ) )
332
- } else {
333
- ControlFlow :: Continue ( Descend :: No )
334
- }
335
- } ,
336
-
337
- // Don't continue into loops or labeled blocks, as they are breakable,
338
- // and we'd have to start checking labels.
339
- ExprKind :: Block ( _, Some ( _) ) | ExprKind :: Loop ( ..) => ControlFlow :: Continue ( Descend :: No ) ,
340
-
341
- // Default: descend
342
- _ => ControlFlow :: Continue ( Descend :: Yes ) ,
343
- } ;
344
- if let ControlFlow :: Continue ( Descend :: Yes ) = self . res {
345
- walk_expr ( self , e) ;
346
- }
347
- }
348
-
349
- fn visit_local ( & mut self , local : & ' tcx Local < ' _ > ) {
350
- // Don't visit the else block of a let/else statement as it will not make
351
- // the statement divergent even though the else block is divergent.
352
- if let Some ( init) = local. init {
353
- self . visit_expr ( init) ;
354
- }
355
- }
356
-
357
- // Avoid unnecessary `walk_*` calls.
358
- fn visit_ty ( & mut self , _: & ' tcx Ty < ' tcx > ) { }
359
- fn visit_pat ( & mut self , _: & ' tcx Pat < ' tcx > ) { }
360
- fn visit_qpath ( & mut self , _: & ' tcx QPath < ' tcx > , _: HirId , _: Span ) { }
361
- // Avoid monomorphising all `visit_*` functions.
362
- fn visit_nested_item ( & mut self , _: ItemId ) { }
363
- }
364
-
365
- let mut v = V {
366
- cx,
367
- res : ControlFlow :: Continue ( Descend :: Yes ) ,
368
- } ;
369
- expr. visit ( & mut v) ;
370
- v. res . is_break ( )
371
- }
372
-
373
272
fn pat_allowed_for_else ( cx : & LateContext < ' _ > , pat : & ' _ Pat < ' _ > , check_types : bool ) -> bool {
374
273
// Check whether the pattern contains any bindings, as the
375
274
// binding might potentially be used in the body.
0 commit comments