@@ -71,6 +71,7 @@ pub use self::hir_utils::{
71
71
both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr , SpanlessEq , SpanlessHash ,
72
72
} ;
73
73
74
+ use core:: mem;
74
75
use core:: ops:: ControlFlow ;
75
76
use std:: collections:: hash_map:: Entry ;
76
77
use std:: hash:: BuildHasherDefault ;
@@ -88,7 +89,7 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
88
89
use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultErr , ResultOk } ;
89
90
use rustc_hir:: {
90
91
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 ,
92
93
ItemKind , LangItem , Local , MatchSource , Mutability , Node , OwnerId , Param , Pat , PatKind , Path , PathSegment , PrimTy ,
93
94
QPath , Stmt , StmtKind , TraitItem , TraitItemKind , TraitItemRef , TraitRef , TyKind , UnOp ,
94
95
} ;
@@ -117,7 +118,7 @@ use crate::ty::{
117
118
adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
118
119
ty_is_fn_once_param,
119
120
} ;
120
- use crate :: visitors:: { for_each_expr, Descend } ;
121
+ use crate :: visitors:: for_each_expr;
121
122
122
123
use rustc_middle:: hir:: nested_filter;
123
124
@@ -2975,100 +2976,263 @@ pub fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: i
2975
2976
}
2976
2977
}
2977
2978
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
+
2980
2999
struct V < ' cx , ' tcx > {
2981
3000
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 ,
3006
+ }
3007
+
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
+ }
2983
3013
}
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
- }
2992
3014
2993
- if self . res . is_break ( ) {
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
+ }
2994
3068
return ;
2995
3069
}
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) => {
3072
+ self . visit_expr ( e) ;
3073
+ return ;
3074
+ } ,
3075
+ ExprKind :: Ret ( None ) => {
3076
+ self . is_never = true ;
3077
+ return ;
3078
+ } ,
3079
+ ExprKind :: Ret ( Some ( e) ) | ExprKind :: Become ( e) => {
3080
+ self . in_final_expr = false ;
3081
+ self . visit_expr ( e) ;
3082
+ self . is_never = true ;
3083
+ return ;
3084
+ } ,
3085
+ ExprKind :: Continue ( _) => {
3086
+ self . is_never = true ;
3087
+ return ;
3088
+ } ,
3089
+ ExprKind :: Break ( dest, e) => {
3090
+ if let Some ( e) = e {
3091
+ self . in_final_expr = false ;
3092
+ self . visit_expr ( e) ;
3008
3093
}
3094
+ if let Ok ( id) = dest. target_id
3095
+ && let Some ( ( i, target) ) = self
3096
+ . break_targets
3097
+ . iter_mut ( )
3098
+ . enumerate ( )
3099
+ . find ( |( _, target) | target. id == id)
3100
+ {
3101
+ target. unused &= self . is_never ;
3102
+ if i < self . break_targets_for_result_ty as usize {
3103
+ self . requires_semi = true ;
3104
+ }
3105
+ }
3106
+ self . is_never = true ;
3107
+ return ;
3009
3108
} ,
3010
- ExprKind :: MethodCall ( ..) => {
3011
- if is_never ( self . cx , e) {
3012
- ControlFlow :: Break ( ( ) )
3109
+ ExprKind :: If ( cond, then, else_) => {
3110
+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3111
+ self . visit_expr ( cond) ;
3112
+ self . in_final_expr = in_final_expr;
3113
+
3114
+ if self . is_never {
3115
+ self . visit_expr ( then) ;
3116
+ if let Some ( else_) = else_ {
3117
+ self . visit_expr ( else_) ;
3118
+ }
3013
3119
} else {
3014
- ControlFlow :: Continue ( Descend :: Yes )
3120
+ self . visit_expr ( then) ;
3121
+ let is_never = mem:: replace ( & mut self . is_never , false ) ;
3122
+ if let Some ( else_) = else_ {
3123
+ self . visit_expr ( else_) ;
3124
+ self . is_never &= is_never;
3125
+ }
3015
3126
}
3127
+ return ;
3016
3128
} ,
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 ( ( ) )
3129
+ ExprKind :: Match ( scrutinee, arms, _) => {
3130
+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3131
+ self . visit_expr ( scrutinee) ;
3132
+ self . in_final_expr = in_final_expr;
3133
+
3134
+ if self . is_never {
3135
+ for arm in arms {
3136
+ self . visit_arm ( arm) ;
3137
+ }
3023
3138
} else {
3024
- ControlFlow :: Continue ( Descend :: No )
3139
+ let mut is_never = true ;
3140
+ for arm in arms {
3141
+ self . is_never = false ;
3142
+ if let Some ( guard) = arm. guard {
3143
+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3144
+ self . visit_expr ( guard. body ( ) ) ;
3145
+ self . in_final_expr = in_final_expr;
3146
+ // The compiler doesn't consider diverging guards as causing the arm to diverge.
3147
+ self . is_never = false ;
3148
+ }
3149
+ self . visit_expr ( arm. body ) ;
3150
+ is_never &= self . is_never ;
3151
+ }
3152
+ self . is_never = is_never;
3025
3153
}
3154
+ return ;
3026
3155
} ,
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 ( ( ) )
3156
+ ExprKind :: Loop ( b, _, _, _) => {
3157
+ self . push_break_target ( e. hir_id ) ;
3158
+ self . in_final_expr = false ;
3159
+ self . visit_block ( b) ;
3160
+ self . is_never = self . break_targets . pop ( ) . unwrap ( ) . unused ;
3161
+ return ;
3162
+ } ,
3163
+ ExprKind :: Block ( b, _) => {
3164
+ if b. targeted_by_break {
3165
+ self . push_break_target ( b. hir_id ) ;
3166
+ self . visit_block ( b) ;
3167
+ self . is_never &= self . break_targets . pop ( ) . unwrap ( ) . unused ;
3035
3168
} else {
3036
- ControlFlow :: Continue ( Descend :: No )
3169
+ self . visit_block ( b ) ;
3037
3170
}
3171
+ return ;
3038
3172
} ,
3173
+ _ => {
3174
+ self . in_final_expr = false ;
3175
+ walk_expr ( self , e) ;
3176
+ self . is_never |= self . cx . typeck_results ( ) . expr_ty ( e) . is_never ( ) ;
3177
+ } ,
3178
+ }
3179
+ }
3039
3180
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) ;
3181
+ fn visit_block ( & mut self , b : & ' tcx Block < ' _ > ) {
3182
+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3183
+ for s in b. stmts {
3184
+ self . visit_stmt ( s) ;
3185
+ }
3186
+ self . in_final_expr = in_final_expr;
3187
+ if let Some ( e) = b. expr {
3188
+ self . visit_expr ( e) ;
3049
3189
}
3050
3190
}
3051
3191
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) ;
3192
+ fn visit_local ( & mut self , l : & ' tcx Local < ' _ > ) {
3193
+ if let Some ( e) = l. init {
3194
+ self . visit_expr ( e) ;
3195
+ }
3196
+ if let Some ( else_) = l. els {
3197
+ let is_never = self . is_never ;
3198
+ self . visit_block ( else_) ;
3199
+ self . is_never = is_never;
3057
3200
}
3058
3201
}
3059
3202
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 ) { }
3203
+ fn visit_arm ( & mut self , arm : & Arm < ' tcx > ) {
3204
+ if let Some ( guard) = arm. guard {
3205
+ let in_final_expr = mem:: replace ( & mut self . in_final_expr , false ) ;
3206
+ self . visit_expr ( guard. body ( ) ) ;
3207
+ self . in_final_expr = in_final_expr;
3208
+ }
3209
+ self . visit_expr ( arm. body ) ;
3210
+ }
3066
3211
}
3067
3212
3068
- let mut v = V {
3069
- cx,
3070
- res : ControlFlow :: Continue ( Descend :: Yes ) ,
3071
- } ;
3072
- expr. visit ( & mut v) ;
3073
- v. res . is_break ( )
3213
+ if cx. typeck_results ( ) . expr_ty ( e) . is_never ( ) {
3214
+ Some ( RequiresSemi :: No )
3215
+ } else if let ExprKind :: Block ( b, _) = e. kind
3216
+ && !b. targeted_by_break
3217
+ && b. expr . is_none ( )
3218
+ {
3219
+ // If a block diverges without a final expression then it's type is `!`.
3220
+ None
3221
+ } else {
3222
+ let mut v = V {
3223
+ cx,
3224
+ break_targets : Vec :: new ( ) ,
3225
+ break_targets_for_result_ty : 0 ,
3226
+ in_final_expr : true ,
3227
+ requires_semi : false ,
3228
+ is_never : false ,
3229
+ } ;
3230
+ v. visit_expr ( e) ;
3231
+ v. is_never
3232
+ . then_some ( if v. requires_semi && matches ! ( e. kind, ExprKind :: Block ( ..) ) {
3233
+ RequiresSemi :: Yes
3234
+ } else {
3235
+ RequiresSemi :: No
3236
+ } )
3237
+ }
3074
3238
}
0 commit comments