@@ -12,6 +12,7 @@ use hir_expand::name;
1212use itertools:: Itertools ;
1313use rustc_hash:: FxHashSet ;
1414use rustc_pattern_analysis:: usefulness:: { compute_match_usefulness, ValidityConstraint } ;
15+ use tracing:: debug;
1516use triomphe:: Arc ;
1617use typed_arena:: Arena ;
1718
@@ -44,6 +45,10 @@ pub enum BodyValidationDiagnostic {
4445 match_expr : ExprId ,
4546 uncovered_patterns : String ,
4647 } ,
48+ NonExhaustiveLet {
49+ pat : PatId ,
50+ uncovered_patterns : String ,
51+ } ,
4752 RemoveTrailingReturn {
4853 return_expr : ExprId ,
4954 } ,
@@ -57,26 +62,25 @@ impl BodyValidationDiagnostic {
5762 let _p =
5863 tracing:: span!( tracing:: Level :: INFO , "BodyValidationDiagnostic::collect" ) . entered ( ) ;
5964 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 ( ) } ;
6167 validator. validate_body ( db) ;
6268 validator. diagnostics
6369 }
6470}
6571
6672struct ExprValidator {
6773 owner : DefWithBodyId ,
74+ body : Arc < Body > ,
6875 infer : Arc < InferenceResult > ,
69- pub ( super ) diagnostics : Vec < BodyValidationDiagnostic > ,
76+ diagnostics : Vec < BodyValidationDiagnostic > ,
7077}
7178
7279impl ExprValidator {
73- fn new ( owner : DefWithBodyId , infer : Arc < InferenceResult > ) -> ExprValidator {
74- ExprValidator { owner, infer, diagnostics : Vec :: new ( ) }
75- }
76-
7780 fn validate_body ( & mut self , db : & dyn HirDatabase ) {
78- let body = db. body ( self . owner ) ;
7981 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 ) ;
8084
8185 if matches ! ( self . owner, DefWithBodyId :: FunctionId ( _) ) {
8286 self . check_for_trailing_return ( body. body_expr , & body) ;
@@ -106,6 +110,9 @@ impl ExprValidator {
106110 Expr :: If { .. } => {
107111 self . check_for_unnecessary_else ( id, expr, & body) ;
108112 }
113+ Expr :: Block { .. } => {
114+ self . validate_block ( db, expr) ;
115+ }
109116 _ => { }
110117 }
111118 }
@@ -162,8 +169,6 @@ impl ExprValidator {
162169 arms : & [ MatchArm ] ,
163170 db : & dyn HirDatabase ,
164171 ) {
165- let body = db. body ( self . owner ) ;
166-
167172 let scrut_ty = & self . infer [ scrutinee_expr] ;
168173 if scrut_ty. is_unknown ( ) {
169174 return ;
@@ -191,12 +196,12 @@ impl ExprValidator {
191196 . as_reference ( )
192197 . map ( |( match_expr_ty, ..) | match_expr_ty == pat_ty)
193198 . 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 )
195200 {
196201 // If we had a NotUsefulMatchArm diagnostic, we could
197202 // check the usefulness of each pattern as we added it
198203 // 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) ;
200205 let m_arm = pat_analysis:: MatchArm {
201206 pat : pattern_arena. alloc ( pat) ,
202207 has_guard : arm. guard . is_some ( ) ,
@@ -234,20 +239,63 @@ impl ExprValidator {
234239 if !witnesses. is_empty ( ) {
235240 self . diagnostics . push ( BodyValidationDiagnostic :: MissingMatchArms {
236241 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 ( ) ) ,
238243 } ) ;
239244 }
240245 }
241246
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+
242291 fn lower_pattern < ' p > (
243292 & self ,
244293 cx : & MatchCheckCtx < ' p > ,
245294 pat : PatId ,
246295 db : & dyn HirDatabase ,
247- body : & Body ,
248296 have_errors : & mut bool ,
249297 ) -> 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 ) ;
251299 let pattern = patcx. lower_pattern ( pat) ;
252300 let pattern = cx. lower_pat ( & pattern) ;
253301 if !patcx. errors . is_empty ( ) {
@@ -448,7 +496,7 @@ fn missing_match_arms<'p>(
448496 cx : & MatchCheckCtx < ' p > ,
449497 scrut_ty : & Ty ,
450498 witnesses : Vec < WitnessPat < ' p > > ,
451- arms : & [ MatchArm ] ,
499+ arms_is_empty : bool ,
452500) -> String {
453501 struct DisplayWitness < ' a , ' p > ( & ' a WitnessPat < ' p > , & ' a MatchCheckCtx < ' p > ) ;
454502 impl fmt:: Display for DisplayWitness < ' _ , ' _ > {
@@ -463,7 +511,7 @@ fn missing_match_arms<'p>(
463511 Some ( ( AdtId :: EnumId ( e) , _) ) => !cx. db . enum_data ( e) . variants . is_empty ( ) ,
464512 _ => false ,
465513 } ;
466- if arms . is_empty ( ) && !non_empty_enum {
514+ if arms_is_empty && !non_empty_enum {
467515 format ! ( "type `{}` is non-empty" , scrut_ty. display( cx. db) )
468516 } else {
469517 let pat_display = |witness| DisplayWitness ( witness, cx) ;
0 commit comments