4
4
5
5
use std:: fmt;
6
6
7
+ use chalk_solve:: rust_ir:: AdtKind ;
7
8
use either:: Either ;
8
- use hir_def:: lang_item:: LangItem ;
9
- use hir_def:: { resolver:: HasResolver , AdtId , AssocItemId , DefWithBodyId , HasModule } ;
10
- use hir_def:: { ItemContainerId , Lookup } ;
9
+ use hir_def:: {
10
+ lang_item:: LangItem ,
11
+ resolver:: { HasResolver , ValueNs } ,
12
+ AdtId , AssocItemId , DefWithBodyId , HasModule , ItemContainerId , Lookup ,
13
+ } ;
11
14
use intern:: sym;
12
15
use itertools:: Itertools ;
13
16
use rustc_hash:: FxHashSet ;
14
17
use rustc_pattern_analysis:: constructor:: Constructor ;
15
- use syntax:: { ast, AstNode } ;
18
+ use syntax:: {
19
+ ast:: { self , UnaryOp } ,
20
+ AstNode ,
21
+ } ;
16
22
use tracing:: debug;
17
23
use triomphe:: Arc ;
18
24
use typed_arena:: Arena ;
19
25
20
- use crate :: Interner ;
21
26
use crate :: {
22
27
db:: HirDatabase ,
23
28
diagnostics:: match_check:: {
24
29
self ,
25
30
pat_analysis:: { self , DeconstructedPat , MatchCheckCtx , WitnessPat } ,
26
31
} ,
27
32
display:: HirDisplay ,
28
- InferenceResult , Ty , TyExt ,
33
+ Adjust , InferenceResult , Interner , Ty , TyExt , TyKind ,
29
34
} ;
30
35
31
36
pub ( crate ) use hir_def:: {
@@ -236,7 +241,12 @@ impl ExprValidator {
236
241
return ;
237
242
}
238
243
239
- let report = match cx. compute_match_usefulness ( m_arms. as_slice ( ) , scrut_ty. clone ( ) ) {
244
+ let known_valid_scrutinee = Some ( self . is_known_valid_scrutinee ( scrutinee_expr, db) ) ;
245
+ let report = match cx. compute_match_usefulness (
246
+ m_arms. as_slice ( ) ,
247
+ scrut_ty. clone ( ) ,
248
+ known_valid_scrutinee,
249
+ ) {
240
250
Ok ( report) => report,
241
251
Err ( ( ) ) => return ,
242
252
} ;
@@ -253,6 +263,45 @@ impl ExprValidator {
253
263
}
254
264
}
255
265
266
+ // [rustc's `is_known_valid_scrutinee`](https://github.com/rust-lang/rust/blob/c9bd03cb724e13cca96ad320733046cbdb16fbbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L288)
267
+ //
268
+ // While the above function in rustc uses thir exprs, r-a doesn't have them.
269
+ // So, the logic here is getting same result as "hir lowering + match with lowered thir"
270
+ // with "hir only"
271
+ fn is_known_valid_scrutinee ( & self , scrutinee_expr : ExprId , db : & dyn HirDatabase ) -> bool {
272
+ if self
273
+ . infer
274
+ . expr_adjustments
275
+ . get ( & scrutinee_expr)
276
+ . is_some_and ( |adjusts| adjusts. iter ( ) . any ( |a| matches ! ( a. kind, Adjust :: Deref ( ..) ) ) )
277
+ {
278
+ return false ;
279
+ }
280
+
281
+ match & self . body [ scrutinee_expr] {
282
+ Expr :: UnaryOp { op : UnaryOp :: Deref , .. } => false ,
283
+ Expr :: Path ( path) => {
284
+ let value_or_partial = self
285
+ . owner
286
+ . resolver ( db. upcast ( ) )
287
+ . resolve_path_in_value_ns_fully ( db. upcast ( ) , path) ;
288
+ value_or_partial. map_or ( true , |v| !matches ! ( v, ValueNs :: StaticId ( _) ) )
289
+ }
290
+ Expr :: Field { expr, .. } => match self . infer . type_of_expr [ * expr] . kind ( Interner ) {
291
+ TyKind :: Adt ( adt, ..)
292
+ if db. adt_datum ( self . owner . krate ( db. upcast ( ) ) , * adt) . kind == AdtKind :: Union =>
293
+ {
294
+ false
295
+ }
296
+ _ => self . is_known_valid_scrutinee ( * expr, db) ,
297
+ } ,
298
+ Expr :: Index { base, .. } => self . is_known_valid_scrutinee ( * base, db) ,
299
+ Expr :: Cast { expr, .. } => self . is_known_valid_scrutinee ( * expr, db) ,
300
+ Expr :: Missing => false ,
301
+ _ => true ,
302
+ }
303
+ }
304
+
256
305
fn validate_block ( & mut self , db : & dyn HirDatabase , expr : & Expr ) {
257
306
let ( Expr :: Block { statements, .. }
258
307
| Expr :: Async { statements, .. }
@@ -285,7 +334,7 @@ impl ExprValidator {
285
334
has_guard : false ,
286
335
arm_data : ( ) ,
287
336
} ;
288
- let report = match cx. compute_match_usefulness ( & [ match_arm] , ty. clone ( ) ) {
337
+ let report = match cx. compute_match_usefulness ( & [ match_arm] , ty. clone ( ) , None ) {
289
338
Ok ( v) => v,
290
339
Err ( e) => {
291
340
debug ! ( ?e, "match usefulness error" ) ;
0 commit comments