@@ -95,7 +95,7 @@ use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet};
95
95
use rustc_index:: vec:: IndexVec ;
96
96
use rustc_middle:: hir:: map:: Map ;
97
97
use rustc_middle:: ty:: query:: Providers ;
98
- use rustc_middle:: ty:: { self , DefIdTree , RootVariableMinCaptureList , TyCtxt } ;
98
+ use rustc_middle:: ty:: { self , DefIdTree , RootVariableMinCaptureList , Ty , TyCtxt } ;
99
99
use rustc_session:: lint;
100
100
use rustc_span:: symbol:: { kw, sym, Symbol } ;
101
101
use rustc_span:: Span ;
@@ -123,8 +123,8 @@ rustc_index::newtype_index! {
123
123
#[ derive( Copy , Clone , PartialEq , Debug ) ]
124
124
enum LiveNodeKind {
125
125
UpvarNode ( Span ) ,
126
- ExprNode ( Span ) ,
127
- VarDefNode ( Span ) ,
126
+ ExprNode ( Span , HirId ) ,
127
+ VarDefNode ( Span , HirId ) ,
128
128
ClosureNode ,
129
129
ExitNode ,
130
130
}
@@ -133,8 +133,8 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
133
133
let sm = tcx. sess . source_map ( ) ;
134
134
match lnk {
135
135
UpvarNode ( s) => format ! ( "Upvar node [{}]" , sm. span_to_diagnostic_string( s) ) ,
136
- ExprNode ( s) => format ! ( "Expr node [{}]" , sm. span_to_diagnostic_string( s) ) ,
137
- VarDefNode ( s) => format ! ( "Var def node [{}]" , sm. span_to_diagnostic_string( s) ) ,
136
+ ExprNode ( s, _ ) => format ! ( "Expr node [{}]" , sm. span_to_diagnostic_string( s) ) ,
137
+ VarDefNode ( s, _ ) => format ! ( "Var def node [{}]" , sm. span_to_diagnostic_string( s) ) ,
138
138
ClosureNode => "Closure node" . to_owned ( ) ,
139
139
ExitNode => "Exit node" . to_owned ( ) ,
140
140
}
@@ -297,7 +297,7 @@ impl IrMaps<'tcx> {
297
297
}
298
298
299
299
pat. each_binding ( |_, hir_id, _, ident| {
300
- self . add_live_node_for_node ( hir_id, VarDefNode ( ident. span ) ) ;
300
+ self . add_live_node_for_node ( hir_id, VarDefNode ( ident. span , hir_id ) ) ;
301
301
self . add_variable ( Local ( LocalInfo {
302
302
id : hir_id,
303
303
name : ident. name ,
@@ -391,14 +391,14 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
391
391
hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, ref path) ) => {
392
392
debug ! ( "expr {}: path that leads to {:?}" , expr. hir_id, path. res) ;
393
393
if let Res :: Local ( _var_hir_id) = path. res {
394
- self . add_live_node_for_node ( expr. hir_id , ExprNode ( expr. span ) ) ;
394
+ self . add_live_node_for_node ( expr. hir_id , ExprNode ( expr. span , expr . hir_id ) ) ;
395
395
}
396
396
intravisit:: walk_expr ( self , expr) ;
397
397
}
398
398
hir:: ExprKind :: Closure ( ..) => {
399
399
// Interesting control flow (for loops can contain labeled
400
400
// breaks or continues)
401
- self . add_live_node_for_node ( expr. hir_id , ExprNode ( expr. span ) ) ;
401
+ self . add_live_node_for_node ( expr. hir_id , ExprNode ( expr. span , expr . hir_id ) ) ;
402
402
403
403
// Make a live_node for each captured variable, with the span
404
404
// being the location that the variable is used. This results
@@ -426,11 +426,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
426
426
427
427
// live nodes required for interesting control flow:
428
428
hir:: ExprKind :: If ( ..) | hir:: ExprKind :: Match ( ..) | hir:: ExprKind :: Loop ( ..) => {
429
- self . add_live_node_for_node ( expr. hir_id , ExprNode ( expr. span ) ) ;
429
+ self . add_live_node_for_node ( expr. hir_id , ExprNode ( expr. span , expr . hir_id ) ) ;
430
430
intravisit:: walk_expr ( self , expr) ;
431
431
}
432
432
hir:: ExprKind :: Binary ( op, ..) if op. node . is_lazy ( ) => {
433
- self . add_live_node_for_node ( expr. hir_id , ExprNode ( expr. span ) ) ;
433
+ self . add_live_node_for_node ( expr. hir_id , ExprNode ( expr. span , expr . hir_id ) ) ;
434
434
intravisit:: walk_expr ( self , expr) ;
435
435
}
436
436
@@ -978,11 +978,26 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
978
978
979
979
hir:: ExprKind :: Call ( ref f, ref args) => {
980
980
let m = self . ir . tcx . parent_module ( expr. hir_id ) . to_def_id ( ) ;
981
- let succ = if self . ir . tcx . is_ty_uninhabited_from (
982
- m,
983
- self . typeck_results . expr_ty ( expr) ,
984
- self . param_env ,
985
- ) {
981
+ let ty = self . typeck_results . expr_ty ( expr) ;
982
+ let succ = if self . ir . tcx . is_ty_uninhabited_from ( m, ty, self . param_env ) {
983
+ if let LiveNodeKind :: ExprNode ( succ_span, succ_id) = self . ir . lnks [ succ] {
984
+ self . warn_about_unreachable (
985
+ expr. span ,
986
+ ty,
987
+ succ_span,
988
+ succ_id,
989
+ "expression" ,
990
+ ) ;
991
+ } else if let LiveNodeKind :: VarDefNode ( succ_span, succ_id) = self . ir . lnks [ succ]
992
+ {
993
+ self . warn_about_unreachable (
994
+ expr. span ,
995
+ ty,
996
+ succ_span,
997
+ succ_id,
998
+ "definition" ,
999
+ ) ;
1000
+ }
986
1001
self . exit_ln
987
1002
} else {
988
1003
succ
@@ -993,11 +1008,26 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
993
1008
994
1009
hir:: ExprKind :: MethodCall ( .., ref args, _) => {
995
1010
let m = self . ir . tcx . parent_module ( expr. hir_id ) . to_def_id ( ) ;
996
- let succ = if self . ir . tcx . is_ty_uninhabited_from (
997
- m,
998
- self . typeck_results . expr_ty ( expr) ,
999
- self . param_env ,
1000
- ) {
1011
+ let ty = self . typeck_results . expr_ty ( expr) ;
1012
+ let succ = if self . ir . tcx . is_ty_uninhabited_from ( m, ty, self . param_env ) {
1013
+ if let LiveNodeKind :: ExprNode ( succ_span, succ_id) = self . ir . lnks [ succ] {
1014
+ self . warn_about_unreachable (
1015
+ expr. span ,
1016
+ ty,
1017
+ succ_span,
1018
+ succ_id,
1019
+ "expression" ,
1020
+ ) ;
1021
+ } else if let LiveNodeKind :: VarDefNode ( succ_span, succ_id) = self . ir . lnks [ succ]
1022
+ {
1023
+ self . warn_about_unreachable (
1024
+ expr. span ,
1025
+ ty,
1026
+ succ_span,
1027
+ succ_id,
1028
+ "definition" ,
1029
+ ) ;
1030
+ }
1001
1031
self . exit_ln
1002
1032
} else {
1003
1033
succ
@@ -1274,6 +1304,47 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
1274
1304
1275
1305
ln
1276
1306
}
1307
+
1308
+ fn warn_about_unreachable (
1309
+ & mut self ,
1310
+ orig_span : Span ,
1311
+ orig_ty : Ty < ' tcx > ,
1312
+ expr_span : Span ,
1313
+ expr_id : HirId ,
1314
+ descr : & str ,
1315
+ ) {
1316
+ if !orig_ty. is_never ( ) {
1317
+ // Unreachable code warnings are already emitted during type checking.
1318
+ // However, during type checking, full type information is being
1319
+ // calculated but not yet available, so the check for diverging
1320
+ // expressions due to uninhabited result types is pretty crude and
1321
+ // only checks whether ty.is_never(). Here, we have full type
1322
+ // information available and can issue warnings for less obviously
1323
+ // uninhabited types (e.g. empty enums). The check above is used so
1324
+ // that we do not emit the same warning twice if the uninhabited type
1325
+ // is indeed `!`.
1326
+
1327
+ self . ir . tcx . struct_span_lint_hir (
1328
+ lint:: builtin:: UNREACHABLE_CODE ,
1329
+ expr_id,
1330
+ expr_span,
1331
+ |lint| {
1332
+ let msg = format ! ( "unreachable {}" , descr) ;
1333
+ lint. build ( & msg)
1334
+ . span_label ( expr_span, & msg)
1335
+ . span_label ( orig_span, "any code following this expression is unreachable" )
1336
+ . span_note (
1337
+ orig_span,
1338
+ & format ! (
1339
+ "this expression has type `{}`, which is uninhabited" ,
1340
+ orig_ty
1341
+ ) ,
1342
+ )
1343
+ . emit ( ) ;
1344
+ } ,
1345
+ ) ;
1346
+ }
1347
+ }
1277
1348
}
1278
1349
1279
1350
// _______________________________________________________________________
0 commit comments