@@ -71,6 +71,7 @@ pub use self::hir_utils::{
7171 both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr , SpanlessEq , SpanlessHash ,
7272} ;
7373
74+ use core:: mem;
7475use core:: ops:: ControlFlow ;
7576use std:: collections:: hash_map:: Entry ;
7677use std:: hash:: BuildHasherDefault ;
@@ -88,7 +89,7 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
8889use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultErr , ResultOk } ;
8990use rustc_hir:: {
9091 self as hir, def, Arm , ArrayLen , BindingAnnotation , Block , BlockCheckMode , Body , Closure , Destination , Expr ,
91- ExprField , ExprKind , FnDecl , FnRetTy , GenericArgs , HirId , Impl , ImplItem , ImplItemKind , ImplItemRef , Item , ItemId ,
92+ ExprField , ExprKind , FnDecl , FnRetTy , GenericArgs , HirId , Impl , ImplItem , ImplItemKind , ImplItemRef , Item ,
9293 ItemKind , LangItem , Local , MatchSource , Mutability , Node , OwnerId , Param , Pat , PatKind , Path , PathSegment , PrimTy ,
9394 QPath , Stmt , StmtKind , TraitItem , TraitItemKind , TraitItemRef , TraitRef , TyKind , UnOp ,
9495} ;
@@ -117,7 +118,7 @@ use crate::ty::{
117118 adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
118119 ty_is_fn_once_param,
119120} ;
120- use crate :: visitors:: { for_each_expr, Descend } ;
121+ use crate :: visitors:: for_each_expr;
121122
122123use rustc_middle:: hir:: nested_filter;
123124
@@ -2975,100 +2976,247 @@ pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: i
29752976 }
29762977}
29772978
2978- /// Check if the expression either returns, or could be coerced into returning, `!`.
2979- pub fn is_never_expr ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
2979+ #[ derive( Clone , Copy ) ]
2980+ pub enum RequiresSemi {
2981+ Yes ,
2982+ No ,
2983+ }
2984+ impl RequiresSemi {
2985+ pub fn requires_semi ( self ) -> bool {
2986+ matches ! ( self , Self :: Yes )
2987+ }
2988+ }
2989+
2990+ /// Check if the expression return `!`, a type coerced from `!`, or could return `!` if the final
2991+ /// expression were turned into a statement.
2992+ #[ expect( clippy:: too_many_lines) ]
2993+ pub fn is_never_expr < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) -> Option < RequiresSemi > {
2994+ struct BreakTarget {
2995+ id : HirId ,
2996+ unused : bool ,
2997+ }
2998+
29802999 struct V < ' cx , ' tcx > {
29813000 cx : & ' cx LateContext < ' tcx > ,
2982- res : ControlFlow < ( ) , Descend > ,
3001+ break_targets : Vec < BreakTarget > ,
3002+ break_targets_for_result_ty : u32 ,
3003+ in_final_expr : bool ,
3004+ requires_semi : bool ,
3005+ is_never : bool ,
29833006 }
2984- impl < ' tcx > Visitor < ' tcx > for V < ' _ , ' _ > {
2985- fn visit_expr ( & mut self , e : & ' tcx Expr < ' tcx > ) {
2986- fn is_never ( cx : & LateContext < ' _ > , expr : & ' _ Expr < ' _ > ) -> bool {
2987- if let Some ( ty) = cx. typeck_results ( ) . expr_ty_opt ( expr) {
2988- return ty. is_never ( ) ;
2989- }
2990- false
2991- }
29923007
2993- if self . res . is_break ( ) {
3008+ impl < ' tcx > V < ' _ , ' tcx > {
3009+ fn push_break_target ( & mut self , id : HirId ) {
3010+ self . break_targets . push ( BreakTarget { id, unused : true } ) ;
3011+ self . break_targets_for_result_ty += u32:: from ( self . in_final_expr ) ;
3012+ }
3013+ }
3014+
3015+ impl < ' tcx > Visitor < ' tcx > for V < ' _ , ' tcx > {
3016+ fn visit_expr ( & mut self , e : & ' tcx Expr < ' _ > ) {
3017+ // Note: Part of the complexity here comes from the fact that
3018+ // coercions are applied to the innermost expression.
3019+ // e.g. In `let x: u32 = { break () };` the never-to-any coercion
3020+ // is applied to the break expression. This means we can't just
3021+ // check the block's type as it will be `u32` despite the fact
3022+ // that the block always diverges.
3023+
3024+ // The rest of the complexity comes from checking blocks which
3025+ // syntactically return a value, but will always diverge before
3026+ // reaching that point.
3027+ // e.g. In `let x = { foo(panic!()) };` the block's type will be the
3028+ // return type of `foo` even though it will never actually run. This
3029+ // can be trivially fixed by adding a semicolon after the call, but
3030+ // we must first detect that a semicolon is needed to make that
3031+ // suggestion.
3032+
3033+ if self . is_never && self . break_targets . is_empty ( ) {
3034+ if self . in_final_expr && !self . requires_semi {
3035+ // This expression won't ever run, but we still need to check
3036+ // if it can affect the type of the final expression.
3037+ match e. kind {
3038+ ExprKind :: DropTemps ( e) => self . visit_expr ( e) ,
3039+ ExprKind :: If ( _, then, Some ( else_) ) => {
3040+ self . visit_expr ( then) ;
3041+ self . visit_expr ( else_) ;
3042+ } ,
3043+ ExprKind :: Match ( _, arms, _) => {
3044+ for arm in arms {
3045+ self . visit_expr ( arm. body ) ;
3046+ }
3047+ } ,
3048+ ExprKind :: Loop ( b, ..) => {
3049+ self . push_break_target ( e. hir_id ) ;
3050+ self . in_final_expr = false ;
3051+ self . visit_block ( b) ;
3052+ self . break_targets . pop ( ) ;
3053+ } ,
3054+ ExprKind :: Block ( b, _) => {
3055+ if b. targeted_by_break {
3056+ self . push_break_target ( b. hir_id ) ;
3057+ self . visit_block ( b) ;
3058+ self . break_targets . pop ( ) ;
3059+ } else {
3060+ self . visit_block ( b) ;
3061+ }
3062+ } ,
3063+ _ => {
3064+ self . requires_semi = !self . cx . typeck_results ( ) . expr_ty ( e) . is_never ( ) ;
3065+ } ,
3066+ }
3067+ }
29943068 return ;
29953069 }
2996-
2997- // We can't just call is_never on expr and be done, because the type system
2998- // sometimes coerces the ! type to something different before we can get
2999- // our hands on it. So instead, we do a manual search. We do fall back to
3000- // is_never in some places when there is no better alternative.
3001- self . res = match e. kind {
3002- ExprKind :: Continue ( _) | ExprKind :: Break ( _, _) | ExprKind :: Ret ( _) => ControlFlow :: Break ( ( ) ) ,
3003- ExprKind :: Call ( call, _) => {
3004- if is_never ( self . cx , e) || is_never ( self . cx , call) {
3005- ControlFlow :: Break ( ( ) )
3006- } else {
3007- ControlFlow :: Continue ( Descend :: Yes )
3070+ match e. kind {
3071+ ExprKind :: DropTemps ( e) => self . visit_expr ( e) ,
3072+ ExprKind :: Ret ( None ) | ExprKind :: Continue ( _) => self . is_never = true ,
3073+ ExprKind :: Ret ( Some ( e) ) | ExprKind :: Become ( e) => {
3074+ self . in_final_expr = false ;
3075+ self . visit_expr ( e) ;
3076+ self . is_never = true ;
3077+ } ,
3078+ ExprKind :: Break ( dest, e) => {
3079+ if let Some ( e) = e {
3080+ self . in_final_expr = false ;
3081+ self . visit_expr ( e) ;
3082+ }
3083+ if let Ok ( id) = dest. target_id
3084+ && let Some ( ( i, target) ) = self
3085+ . break_targets
3086+ . iter_mut ( )
3087+ . enumerate ( )
3088+ . find ( |( _, target) | target. id == id)
3089+ {
3090+ target. unused &= self . is_never ;
3091+ if i < self . break_targets_for_result_ty as usize {
3092+ self . requires_semi = true ;
3093+ }
30083094 }
3095+ self . is_never = true ;
30093096 } ,
3010- ExprKind :: MethodCall ( ..) => {
3011- if is_never ( self . cx , e) {
3012- ControlFlow :: Break ( ( ) )
3097+ ExprKind :: If ( cond, then, else_) => {
3098+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3099+ self . visit_expr ( cond) ;
3100+ self . in_final_expr = in_final_expr;
3101+
3102+ if self . is_never {
3103+ self . visit_expr ( then) ;
3104+ if let Some ( else_) = else_ {
3105+ self . visit_expr ( else_) ;
3106+ }
30133107 } else {
3014- ControlFlow :: Continue ( Descend :: Yes )
3108+ self . visit_expr ( then) ;
3109+ let is_never = mem:: replace ( & mut self . is_never , false ) ;
3110+ if let Some ( else_) = else_ {
3111+ self . visit_expr ( else_) ;
3112+ self . is_never &= is_never;
3113+ }
30153114 }
30163115 } ,
3017- ExprKind :: If ( if_expr, if_then, if_else) => {
3018- let else_diverges = if_else. map_or ( false , |ex| is_never_expr ( self . cx , ex) ) ;
3019- let diverges =
3020- is_never_expr ( self . cx , if_expr) || ( else_diverges && is_never_expr ( self . cx , if_then) ) ;
3021- if diverges {
3022- ControlFlow :: Break ( ( ) )
3116+ ExprKind :: Match ( scrutinee, arms, _) => {
3117+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3118+ self . visit_expr ( scrutinee) ;
3119+ self . in_final_expr = in_final_expr;
3120+
3121+ if self . is_never {
3122+ for arm in arms {
3123+ self . visit_arm ( arm) ;
3124+ }
30233125 } else {
3024- ControlFlow :: Continue ( Descend :: No )
3126+ let mut is_never = true ;
3127+ for arm in arms {
3128+ self . is_never = false ;
3129+ if let Some ( guard) = arm. guard {
3130+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3131+ self . visit_expr ( guard. body ( ) ) ;
3132+ self . in_final_expr = in_final_expr;
3133+ // The compiler doesn't consider diverging guards as causing the arm to diverge.
3134+ self . is_never = false ;
3135+ }
3136+ self . visit_expr ( arm. body ) ;
3137+ is_never &= self . is_never ;
3138+ }
3139+ self . is_never = is_never;
30253140 }
30263141 } ,
3027- ExprKind :: Match ( match_expr, match_arms, _) => {
3028- let diverges = is_never_expr ( self . cx , match_expr)
3029- || match_arms. iter ( ) . all ( |arm| {
3030- let guard_diverges = arm. guard . as_ref ( ) . map_or ( false , |g| is_never_expr ( self . cx , g. body ( ) ) ) ;
3031- guard_diverges || is_never_expr ( self . cx , arm. body )
3032- } ) ;
3033- if diverges {
3034- ControlFlow :: Break ( ( ) )
3142+ ExprKind :: Loop ( b, _, _, _) => {
3143+ self . push_break_target ( e. hir_id ) ;
3144+ self . in_final_expr = false ;
3145+ self . visit_block ( b) ;
3146+ self . is_never = self . break_targets . pop ( ) . unwrap ( ) . unused ;
3147+ } ,
3148+ ExprKind :: Block ( b, _) => {
3149+ if b. targeted_by_break {
3150+ self . push_break_target ( b. hir_id ) ;
3151+ self . visit_block ( b) ;
3152+ self . is_never &= self . break_targets . pop ( ) . unwrap ( ) . unused ;
30353153 } else {
3036- ControlFlow :: Continue ( Descend :: No )
3154+ self . visit_block ( b ) ;
30373155 }
30383156 } ,
3157+ _ => {
3158+ self . in_final_expr = false ;
3159+ walk_expr ( self , e) ;
3160+ self . is_never |= self . cx . typeck_results ( ) . expr_ty ( e) . is_never ( ) ;
3161+ } ,
3162+ }
3163+ }
30393164
3040- // Don't continue into loops or labeled blocks, as they are breakable,
3041- // and we'd have to start checking labels.
3042- ExprKind :: Block ( _, Some ( _) ) | ExprKind :: Loop ( ..) => ControlFlow :: Continue ( Descend :: No ) ,
3043-
3044- // Default: descend
3045- _ => ControlFlow :: Continue ( Descend :: Yes ) ,
3046- } ;
3047- if let ControlFlow :: Continue ( Descend :: Yes ) = self . res {
3048- walk_expr ( self , e) ;
3165+ fn visit_block ( & mut self , b : & ' tcx Block < ' _ > ) {
3166+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3167+ for s in b. stmts {
3168+ self . visit_stmt ( s) ;
3169+ }
3170+ self . in_final_expr = in_final_expr;
3171+ if let Some ( e) = b. expr {
3172+ self . visit_expr ( e) ;
30493173 }
30503174 }
30513175
3052- fn visit_local ( & mut self , local : & ' tcx Local < ' _ > ) {
3053- // Don't visit the else block of a let/else statement as it will not make
3054- // the statement divergent even though the else block is divergent.
3055- if let Some ( init) = local. init {
3056- self . visit_expr ( init) ;
3176+ fn visit_local ( & mut self , l : & ' tcx Local < ' _ > ) {
3177+ if let Some ( e) = l. init {
3178+ self . visit_expr ( e) ;
3179+ }
3180+ if let Some ( else_) = l. els {
3181+ let is_never = self . is_never ;
3182+ self . visit_block ( else_) ;
3183+ self . is_never = is_never;
30573184 }
30583185 }
30593186
3060- // Avoid unnecessary `walk_*` calls.
3061- fn visit_ty ( & mut self , _: & ' tcx hir:: Ty < ' tcx > ) { }
3062- fn visit_pat ( & mut self , _: & ' tcx Pat < ' tcx > ) { }
3063- fn visit_qpath ( & mut self , _: & ' tcx QPath < ' tcx > , _: HirId , _: Span ) { }
3064- // Avoid monomorphising all `visit_*` functions.
3065- fn visit_nested_item ( & mut self , _: ItemId ) { }
3187+ fn visit_arm ( & mut self , arm : & Arm < ' tcx > ) {
3188+ if let Some ( guard) = arm. guard {
3189+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3190+ self . visit_expr ( guard. body ( ) ) ;
3191+ self . in_final_expr = in_final_expr;
3192+ }
3193+ self . visit_expr ( arm. body ) ;
3194+ }
30663195 }
30673196
3068- let mut v = V {
3069- cx,
3070- res : ControlFlow :: Continue ( Descend :: Yes ) ,
3071- } ;
3072- expr. visit ( & mut v) ;
3073- v. res . is_break ( )
3197+ if cx. typeck_results ( ) . expr_ty ( e) . is_never ( ) {
3198+ Some ( RequiresSemi :: No )
3199+ } else if let ExprKind :: Block ( b, _) = e. kind
3200+ && !b. targeted_by_break
3201+ && b. expr . is_none ( )
3202+ {
3203+ // If a block diverges without a final expression then it's type is `!`.
3204+ None
3205+ } else {
3206+ let mut v = V {
3207+ cx,
3208+ break_targets : Vec :: new ( ) ,
3209+ break_targets_for_result_ty : 0 ,
3210+ in_final_expr : true ,
3211+ requires_semi : false ,
3212+ is_never : false ,
3213+ } ;
3214+ v. visit_expr ( e) ;
3215+ v. is_never
3216+ . then_some ( if v. requires_semi && matches ! ( e. kind, ExprKind :: Block ( ..) ) {
3217+ RequiresSemi :: Yes
3218+ } else {
3219+ RequiresSemi :: No
3220+ } )
3221+ }
30743222}
0 commit comments