@@ -5,18 +5,15 @@ use clippy_utils::diagnostics::span_lint_and_then;
55use clippy_utils:: higher:: IfLetOrMatch ;
66use clippy_utils:: source:: snippet_with_context;
77use 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} ;
109use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
1110use 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 } ;
1412use rustc_lint:: { LateContext , LintContext } ;
1513use rustc_middle:: lint:: in_external_macro;
1614use rustc_session:: declare_tool_lint;
1715use rustc_span:: symbol:: { sym, Symbol } ;
1816use rustc_span:: Span ;
19- use std:: ops:: ControlFlow ;
2017use std:: slice;
2118
2219declare_clippy_lint ! {
@@ -51,7 +48,7 @@ declare_clippy_lint! {
5148}
5249
5350impl < ' 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 > ) {
5552 if !self . msrv . meets ( msrvs:: LET_ELSE ) || in_external_macro ( cx. sess ( ) , stmt. span ) {
5653 return ;
5754 }
@@ -67,7 +64,7 @@ impl<'tcx> QuestionMark {
6764 IfLetOrMatch :: IfLet ( if_let_expr, let_pat, if_then, if_else) => {
6865 if let Some ( ident_map) = expr_simple_identity_map ( local. pat , let_pat, if_then)
6966 && let Some ( if_else) = if_else
70- && expr_diverges ( cx, if_else)
67+ && is_never_expr ( cx, if_else) . is_some ( )
7168 && let qm_allowed = is_lint_allowed ( cx, QUESTION_MARK , stmt. hir_id )
7269 && ( qm_allowed || pat_and_expr_can_be_question_mark ( cx, let_pat, if_else) . is_none ( ) )
7370 {
@@ -91,10 +88,9 @@ impl<'tcx> QuestionMark {
9188 return ;
9289 }
9390 let check_types = self . matches_behaviour == MatchLintBehaviour :: WellKnownTypes ;
94- let diverging_arm_opt = arms
95- . iter ( )
96- . enumerate ( )
97- . find ( |( _, arm) | expr_diverges ( cx, arm. body ) && pat_allowed_for_else ( cx, arm. pat , check_types) ) ;
91+ let diverging_arm_opt = arms. iter ( ) . enumerate ( ) . find ( |( _, arm) | {
92+ is_never_expr ( cx, arm. body ) . is_some ( ) && pat_allowed_for_else ( cx, arm. pat , check_types)
93+ } ) ;
9894 let Some ( ( idx, diverging_arm) ) = diverging_arm_opt else {
9995 return ;
10096 } ;
@@ -272,104 +268,6 @@ fn replace_in_pattern(
272268 sn_pat. into_owned ( )
273269}
274270
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-
373271fn pat_allowed_for_else ( cx : & LateContext < ' _ > , pat : & ' _ Pat < ' _ > , check_types : bool ) -> bool {
374272 // Check whether the pattern contains any bindings, as the
375273 // binding might potentially be used in the body.
0 commit comments