@@ -12,6 +12,7 @@ use hir_expand::name;
12
12
use itertools:: Itertools ;
13
13
use rustc_hash:: FxHashSet ;
14
14
use rustc_pattern_analysis:: usefulness:: { compute_match_usefulness, ValidityConstraint } ;
15
+ use tracing:: debug;
15
16
use triomphe:: Arc ;
16
17
use typed_arena:: Arena ;
17
18
@@ -44,6 +45,10 @@ pub enum BodyValidationDiagnostic {
44
45
match_expr : ExprId ,
45
46
uncovered_patterns : String ,
46
47
} ,
48
+ NonExhaustiveLet {
49
+ pat : PatId ,
50
+ uncovered_patterns : String ,
51
+ } ,
47
52
RemoveTrailingReturn {
48
53
return_expr : ExprId ,
49
54
} ,
@@ -68,7 +73,7 @@ struct ExprValidator {
68
73
owner : DefWithBodyId ,
69
74
body : Arc < Body > ,
70
75
infer : Arc < InferenceResult > ,
71
- pub ( super ) diagnostics : Vec < BodyValidationDiagnostic > ,
76
+ diagnostics : Vec < BodyValidationDiagnostic > ,
72
77
}
73
78
74
79
impl ExprValidator {
@@ -105,6 +110,9 @@ impl ExprValidator {
105
110
Expr :: If { .. } => {
106
111
self . check_for_unnecessary_else ( id, expr, & body) ;
107
112
}
113
+ Expr :: Block { .. } => {
114
+ self . validate_block ( db, expr) ;
115
+ }
108
116
_ => { }
109
117
}
110
118
}
@@ -231,11 +239,55 @@ impl ExprValidator {
231
239
if !witnesses. is_empty ( ) {
232
240
self . diagnostics . push ( BodyValidationDiagnostic :: MissingMatchArms {
233
241
match_expr,
234
- uncovered_patterns : missing_match_arms ( & cx, scrut_ty, witnesses, arms ) ,
242
+ uncovered_patterns : missing_match_arms ( & cx, scrut_ty, witnesses, m_arms . is_empty ( ) ) ,
235
243
} ) ;
236
244
}
237
245
}
238
246
247
+ fn validate_block ( & mut self , db : & dyn HirDatabase , expr : & Expr ) {
248
+ let Expr :: Block { statements, .. } = expr else { return } ;
249
+ let pattern_arena = Arena :: new ( ) ;
250
+ let cx = MatchCheckCtx :: new ( self . owner . module ( db. upcast ( ) ) , self . owner , db) ;
251
+ for stmt in & * * statements {
252
+ let & Statement :: Let { pat, initializer, else_branch : None , .. } = stmt else {
253
+ continue ;
254
+ } ;
255
+ let Some ( initializer) = initializer else { continue } ;
256
+ let ty = & self . infer [ initializer] ;
257
+
258
+ let mut have_errors = false ;
259
+ let deconstructed_pat = self . lower_pattern ( & cx, pat, db, & mut have_errors) ;
260
+ let match_arm = rustc_pattern_analysis:: MatchArm {
261
+ pat : pattern_arena. alloc ( deconstructed_pat) ,
262
+ has_guard : false ,
263
+ arm_data : ( ) ,
264
+ } ;
265
+ if have_errors {
266
+ continue ;
267
+ }
268
+
269
+ let report = match compute_match_usefulness (
270
+ & cx,
271
+ & [ match_arm] ,
272
+ ty. clone ( ) ,
273
+ ValidityConstraint :: ValidOnly ,
274
+ ) {
275
+ Ok ( v) => v,
276
+ Err ( e) => {
277
+ debug ! ( ?e, "match usefulness error" ) ;
278
+ continue ;
279
+ }
280
+ } ;
281
+ let witnesses = report. non_exhaustiveness_witnesses ;
282
+ if !witnesses. is_empty ( ) {
283
+ self . diagnostics . push ( BodyValidationDiagnostic :: NonExhaustiveLet {
284
+ pat,
285
+ uncovered_patterns : missing_match_arms ( & cx, ty, witnesses, false ) ,
286
+ } ) ;
287
+ }
288
+ }
289
+ }
290
+
239
291
fn lower_pattern < ' p > (
240
292
& self ,
241
293
cx : & MatchCheckCtx < ' p > ,
@@ -444,7 +496,7 @@ fn missing_match_arms<'p>(
444
496
cx : & MatchCheckCtx < ' p > ,
445
497
scrut_ty : & Ty ,
446
498
witnesses : Vec < WitnessPat < ' p > > ,
447
- arms : & [ MatchArm ] ,
499
+ arms_is_empty : bool ,
448
500
) -> String {
449
501
struct DisplayWitness < ' a , ' p > ( & ' a WitnessPat < ' p > , & ' a MatchCheckCtx < ' p > ) ;
450
502
impl fmt:: Display for DisplayWitness < ' _ , ' _ > {
@@ -459,7 +511,7 @@ fn missing_match_arms<'p>(
459
511
Some ( ( AdtId :: EnumId ( e) , _) ) => !cx. db . enum_data ( e) . variants . is_empty ( ) ,
460
512
_ => false ,
461
513
} ;
462
- if arms . is_empty ( ) && !non_empty_enum {
514
+ if arms_is_empty && !non_empty_enum {
463
515
format ! ( "type `{}` is non-empty" , scrut_ty. display( cx. db) )
464
516
} else {
465
517
let pat_display = |witness| DisplayWitness ( witness, cx) ;
0 commit comments