@@ -223,7 +223,7 @@ use hir_def::{
223223 adt:: VariantData ,
224224 body:: Body ,
225225 expr:: { Expr , Literal , Pat , PatId } ,
226- AdtId , EnumVariantId , VariantId ,
226+ AdtId , EnumVariantId , StructId , VariantId ,
227227} ;
228228use smallvec:: { smallvec, SmallVec } ;
229229
@@ -391,21 +391,28 @@ impl PatStack {
391391 }
392392 }
393393 ( Pat :: Wild , constructor) => Some ( self . expand_wildcard ( cx, constructor) ?) ,
394- ( Pat :: Path ( _) , Constructor :: Enum ( constructor) ) => {
394+ ( Pat :: Path ( _) , constructor) => {
395395 // unit enum variants become `Pat::Path`
396396 let pat_id = head. as_id ( ) . expect ( "we know this isn't a wild" ) ;
397- if !enum_variant_matches ( cx, pat_id, * constructor) {
397+ let variant_id: VariantId = match constructor {
398+ & Constructor :: Enum ( e) => e. into ( ) ,
399+ & Constructor :: Struct ( s) => s. into ( ) ,
400+ _ => return Err ( MatchCheckErr :: NotImplemented ) ,
401+ } ;
402+ if Some ( variant_id) != cx. infer . variant_resolution_for_pat ( pat_id) {
398403 None
399404 } else {
400405 Some ( self . to_tail ( ) )
401406 }
402407 }
403- (
404- Pat :: TupleStruct { args : ref pat_ids, ellipsis, .. } ,
405- Constructor :: Enum ( enum_constructor) ,
406- ) => {
408+ ( Pat :: TupleStruct { args : ref pat_ids, ellipsis, .. } , constructor) => {
407409 let pat_id = head. as_id ( ) . expect ( "we know this isn't a wild" ) ;
408- if !enum_variant_matches ( cx, pat_id, * enum_constructor) {
410+ let variant_id: VariantId = match constructor {
411+ & Constructor :: Enum ( e) => e. into ( ) ,
412+ & Constructor :: Struct ( s) => s. into ( ) ,
413+ _ => return Err ( MatchCheckErr :: MalformedMatchArm ) ,
414+ } ;
415+ if Some ( variant_id) != cx. infer . variant_resolution_for_pat ( pat_id) {
409416 None
410417 } else {
411418 let constructor_arity = constructor. arity ( cx) ?;
@@ -443,12 +450,22 @@ impl PatStack {
443450 }
444451 }
445452 }
446- ( Pat :: Record { args : ref arg_patterns, .. } , Constructor :: Enum ( e ) ) => {
453+ ( Pat :: Record { args : ref arg_patterns, .. } , constructor ) => {
447454 let pat_id = head. as_id ( ) . expect ( "we know this isn't a wild" ) ;
448- if !enum_variant_matches ( cx, pat_id, * e) {
455+ let ( variant_id, variant_data) = match constructor {
456+ & Constructor :: Enum ( e) => (
457+ e. into ( ) ,
458+ cx. db . enum_data ( e. parent ) . variants [ e. local_id ] . variant_data . clone ( ) ,
459+ ) ,
460+ & Constructor :: Struct ( s) => {
461+ ( s. into ( ) , cx. db . struct_data ( s) . variant_data . clone ( ) )
462+ }
463+ _ => return Err ( MatchCheckErr :: MalformedMatchArm ) ,
464+ } ;
465+ if Some ( variant_id) != cx. infer . variant_resolution_for_pat ( pat_id) {
449466 None
450467 } else {
451- match cx . db . enum_data ( e . parent ) . variants [ e . local_id ] . variant_data . as_ref ( ) {
468+ match variant_data. as_ref ( ) {
452469 VariantData :: Record ( struct_field_arena) => {
453470 // Here we treat any missing fields in the record as the wild pattern, as
454471 // if the record has ellipsis. We want to do this here even if the
@@ -727,6 +744,7 @@ enum Constructor {
727744 Bool ( bool ) ,
728745 Tuple { arity : usize } ,
729746 Enum ( EnumVariantId ) ,
747+ Struct ( StructId ) ,
730748}
731749
732750impl Constructor {
@@ -741,6 +759,11 @@ impl Constructor {
741759 VariantData :: Unit => 0 ,
742760 }
743761 }
762+ & Constructor :: Struct ( s) => match cx. db . struct_data ( s) . variant_data . as_ref ( ) {
763+ VariantData :: Tuple ( struct_field_data) => struct_field_data. len ( ) ,
764+ VariantData :: Record ( struct_field_data) => struct_field_data. len ( ) ,
765+ VariantData :: Unit => 0 ,
766+ } ,
744767 } ;
745768
746769 Ok ( arity)
@@ -749,7 +772,7 @@ impl Constructor {
749772 fn all_constructors ( & self , cx : & MatchCheckCtx ) -> Vec < Constructor > {
750773 match self {
751774 Constructor :: Bool ( _) => vec ! [ Constructor :: Bool ( true ) , Constructor :: Bool ( false ) ] ,
752- Constructor :: Tuple { .. } => vec ! [ * self ] ,
775+ Constructor :: Tuple { .. } | Constructor :: Struct ( _ ) => vec ! [ * self ] ,
753776 Constructor :: Enum ( e) => cx
754777 . db
755778 . enum_data ( e. parent )
@@ -786,6 +809,7 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Opt
786809 VariantId :: EnumVariantId ( enum_variant_id) => {
787810 Some ( Constructor :: Enum ( enum_variant_id) )
788811 }
812+ VariantId :: StructId ( struct_id) => Some ( Constructor :: Struct ( struct_id) ) ,
789813 _ => return Err ( MatchCheckErr :: NotImplemented ) ,
790814 }
791815 }
@@ -830,13 +854,13 @@ fn all_constructors_covered(
830854
831855 false
832856 } ) ,
857+ & Constructor :: Struct ( s) => used_constructors. iter ( ) . any ( |constructor| match constructor {
858+ & Constructor :: Struct ( sid) => sid == s,
859+ _ => false ,
860+ } ) ,
833861 }
834862}
835863
836- fn enum_variant_matches ( cx : & MatchCheckCtx , pat_id : PatId , enum_variant_id : EnumVariantId ) -> bool {
837- Some ( enum_variant_id. into ( ) ) == cx. infer . variant_resolution_for_pat ( pat_id)
838- }
839-
840864#[ cfg( test) ]
841865mod tests {
842866 use crate :: diagnostics:: tests:: check_diagnostics;
@@ -848,8 +872,8 @@ mod tests {
848872fn main() {
849873 match () { }
850874 //^^ Missing match arm
851- match (()) { }
852- //^^^^ Missing match arm
875+ match (()) { }
876+ //^^^^ Missing match arm
853877
854878 match () { _ => (), }
855879 match () { () => (), }
@@ -1393,6 +1417,84 @@ fn main() {
13931417 ) ;
13941418 }
13951419
1420+ #[ test]
1421+ fn record_struct ( ) {
1422+ check_diagnostics (
1423+ r#"struct Foo { a: bool }
1424+ fn main(f: Foo) {
1425+ match f {}
1426+ //^ Missing match arm
1427+ match f { Foo { a: true } => () }
1428+ //^ Missing match arm
1429+ match &f { Foo { a: true } => () }
1430+ //^^ Missing match arm
1431+ match f { Foo { a: _ } => () }
1432+ match f {
1433+ Foo { a: true } => (),
1434+ Foo { a: false } => (),
1435+ }
1436+ match &f {
1437+ Foo { a: true } => (),
1438+ Foo { a: false } => (),
1439+ }
1440+ }
1441+ "# ,
1442+ ) ;
1443+ }
1444+
1445+ #[ test]
1446+ fn tuple_struct ( ) {
1447+ check_diagnostics (
1448+ r#"struct Foo(bool);
1449+ fn main(f: Foo) {
1450+ match f {}
1451+ //^ Missing match arm
1452+ match f { Foo(true) => () }
1453+ //^ Missing match arm
1454+ match f {
1455+ Foo(true) => (),
1456+ Foo(false) => (),
1457+ }
1458+ }
1459+ "# ,
1460+ ) ;
1461+ }
1462+
1463+ #[ test]
1464+ fn unit_struct ( ) {
1465+ check_diagnostics (
1466+ r#"struct Foo;
1467+ fn main(f: Foo) {
1468+ match f {}
1469+ //^ Missing match arm
1470+ match f { Foo => () }
1471+ }
1472+ "# ,
1473+ ) ;
1474+ }
1475+
1476+ #[ test]
1477+ fn record_struct_ellipsis ( ) {
1478+ check_diagnostics (
1479+ r#"struct Foo { foo: bool, bar: bool }
1480+ fn main(f: Foo) {
1481+ match f { Foo { foo: true, .. } => () }
1482+ //^ Missing match arm
1483+ match f {
1484+ //^ Missing match arm
1485+ Foo { foo: true, .. } => (),
1486+ Foo { bar: false, .. } => ()
1487+ }
1488+ match f { Foo { .. } => () }
1489+ match f {
1490+ Foo { foo: true, .. } => (),
1491+ Foo { foo: false, .. } => ()
1492+ }
1493+ }
1494+ "# ,
1495+ ) ;
1496+ }
1497+
13961498 mod false_negatives {
13971499 //! The implementation of match checking here is a work in progress. As we roll this out, we
13981500 //! prefer false negatives to false positives (ideally there would be no false positives). This
@@ -1431,19 +1533,6 @@ fn main() {
14311533 Either::A(true | false) => (),
14321534 }
14331535}
1434- "# ,
1435- ) ;
1436- }
1437-
1438- #[ test]
1439- fn struct_missing_arm ( ) {
1440- // We don't currently handle structs.
1441- check_diagnostics (
1442- r#"
1443- struct Foo { a: bool }
1444- fn main(f: Foo) {
1445- match f { Foo { a: true } => () }
1446- }
14471536"# ,
14481537 ) ;
14491538 }
0 commit comments