@@ -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,247 @@ 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 ,
2983
3006
}
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
3007
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
+ }
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) => 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
+ }
3008
3094
}
3095
+ self . is_never = true ;
3009
3096
} ,
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
+ }
3013
3107
} 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
+ }
3015
3114
}
3016
3115
} ,
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
+ }
3023
3125
} 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;
3025
3140
}
3026
3141
} ,
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 ;
3035
3153
} else {
3036
- ControlFlow :: Continue ( Descend :: No )
3154
+ self . visit_block ( b ) ;
3037
3155
}
3038
3156
} ,
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
+ }
3039
3164
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) ;
3049
3173
}
3050
3174
}
3051
3175
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;
3057
3184
}
3058
3185
}
3059
3186
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
+ }
3066
3195
}
3067
3196
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
+ }
3074
3222
}
0 commit comments