@@ -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
} ,
@@ -57,26 +62,25 @@ impl BodyValidationDiagnostic {
57
62
let _p =
58
63
tracing:: span!( tracing:: Level :: INFO , "BodyValidationDiagnostic::collect" ) . entered ( ) ;
59
64
let infer = db. infer ( owner) ;
60
- let mut validator = ExprValidator :: new ( owner, infer) ;
65
+ let body = db. body ( owner) ;
66
+ let mut validator = ExprValidator { owner, body, infer, diagnostics : Vec :: new ( ) } ;
61
67
validator. validate_body ( db) ;
62
68
validator. diagnostics
63
69
}
64
70
}
65
71
66
72
struct ExprValidator {
67
73
owner : DefWithBodyId ,
74
+ body : Arc < Body > ,
68
75
infer : Arc < InferenceResult > ,
69
- pub ( super ) diagnostics : Vec < BodyValidationDiagnostic > ,
76
+ diagnostics : Vec < BodyValidationDiagnostic > ,
70
77
}
71
78
72
79
impl ExprValidator {
73
- fn new ( owner : DefWithBodyId , infer : Arc < InferenceResult > ) -> ExprValidator {
74
- ExprValidator { owner, infer, diagnostics : Vec :: new ( ) }
75
- }
76
-
77
80
fn validate_body ( & mut self , db : & dyn HirDatabase ) {
78
- let body = db. body ( self . owner ) ;
79
81
let mut filter_map_next_checker = None ;
82
+ // we'll pass &mut self while iterating over body.exprs, so they need to be disjoint
83
+ let body = Arc :: clone ( & self . body ) ;
80
84
81
85
if matches ! ( self . owner, DefWithBodyId :: FunctionId ( _) ) {
82
86
self . check_for_trailing_return ( body. body_expr , & body) ;
@@ -106,6 +110,9 @@ impl ExprValidator {
106
110
Expr :: If { .. } => {
107
111
self . check_for_unnecessary_else ( id, expr, & body) ;
108
112
}
113
+ Expr :: Block { .. } => {
114
+ self . validate_block ( db, expr) ;
115
+ }
109
116
_ => { }
110
117
}
111
118
}
@@ -162,8 +169,6 @@ impl ExprValidator {
162
169
arms : & [ MatchArm ] ,
163
170
db : & dyn HirDatabase ,
164
171
) {
165
- let body = db. body ( self . owner ) ;
166
-
167
172
let scrut_ty = & self . infer [ scrutinee_expr] ;
168
173
if scrut_ty. is_unknown ( ) {
169
174
return ;
@@ -191,12 +196,12 @@ impl ExprValidator {
191
196
. as_reference ( )
192
197
. map ( |( match_expr_ty, ..) | match_expr_ty == pat_ty)
193
198
. unwrap_or ( false ) )
194
- && types_of_subpatterns_do_match ( arm. pat , & body, & self . infer )
199
+ && types_of_subpatterns_do_match ( arm. pat , & self . body , & self . infer )
195
200
{
196
201
// If we had a NotUsefulMatchArm diagnostic, we could
197
202
// check the usefulness of each pattern as we added it
198
203
// to the matrix here.
199
- let pat = self . lower_pattern ( & cx, arm. pat , db, & body , & mut has_lowering_errors) ;
204
+ let pat = self . lower_pattern ( & cx, arm. pat , db, & mut has_lowering_errors) ;
200
205
let m_arm = pat_analysis:: MatchArm {
201
206
pat : pattern_arena. alloc ( pat) ,
202
207
has_guard : arm. guard . is_some ( ) ,
@@ -234,20 +239,63 @@ impl ExprValidator {
234
239
if !witnesses. is_empty ( ) {
235
240
self . diagnostics . push ( BodyValidationDiagnostic :: MissingMatchArms {
236
241
match_expr,
237
- uncovered_patterns : missing_match_arms ( & cx, scrut_ty, witnesses, arms ) ,
242
+ uncovered_patterns : missing_match_arms ( & cx, scrut_ty, witnesses, m_arms . is_empty ( ) ) ,
238
243
} ) ;
239
244
}
240
245
}
241
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
+
242
291
fn lower_pattern < ' p > (
243
292
& self ,
244
293
cx : & MatchCheckCtx < ' p > ,
245
294
pat : PatId ,
246
295
db : & dyn HirDatabase ,
247
- body : & Body ,
248
296
have_errors : & mut bool ,
249
297
) -> DeconstructedPat < ' p > {
250
- let mut patcx = match_check:: PatCtxt :: new ( db, & self . infer , body) ;
298
+ let mut patcx = match_check:: PatCtxt :: new ( db, & self . infer , & self . body ) ;
251
299
let pattern = patcx. lower_pattern ( pat) ;
252
300
let pattern = cx. lower_pat ( & pattern) ;
253
301
if !patcx. errors . is_empty ( ) {
@@ -448,7 +496,7 @@ fn missing_match_arms<'p>(
448
496
cx : & MatchCheckCtx < ' p > ,
449
497
scrut_ty : & Ty ,
450
498
witnesses : Vec < WitnessPat < ' p > > ,
451
- arms : & [ MatchArm ] ,
499
+ arms_is_empty : bool ,
452
500
) -> String {
453
501
struct DisplayWitness < ' a , ' p > ( & ' a WitnessPat < ' p > , & ' a MatchCheckCtx < ' p > ) ;
454
502
impl fmt:: Display for DisplayWitness < ' _ , ' _ > {
@@ -463,7 +511,7 @@ fn missing_match_arms<'p>(
463
511
Some ( ( AdtId :: EnumId ( e) , _) ) => !cx. db . enum_data ( e) . variants . is_empty ( ) ,
464
512
_ => false ,
465
513
} ;
466
- if arms . is_empty ( ) && !non_empty_enum {
514
+ if arms_is_empty && !non_empty_enum {
467
515
format ! ( "type `{}` is non-empty" , scrut_ty. display( cx. db) )
468
516
} else {
469
517
let pat_display = |witness| DisplayWitness ( witness, cx) ;
0 commit comments