11use std:: iter:: { self , Peekable } ;
22
33use either:: Either ;
4- use hir:: { Adt , Crate , HasAttrs , ImportPathConfig , ModuleDef , Semantics , sym} ;
4+ use hir:: { Adt , AsAssocItem , Crate , HasAttrs , ImportPathConfig , ModuleDef , Semantics , sym} ;
55use ide_db:: RootDatabase ;
66use ide_db:: assists:: ExprFillDefaultMode ;
77use ide_db:: syntax_helpers:: suggest_name;
88use ide_db:: { famous_defs:: FamousDefs , helpers:: mod_path_to_ast} ;
99use itertools:: Itertools ;
10+ use syntax:: ToSmolStr ;
1011use syntax:: ast:: edit:: IndentLevel ;
1112use syntax:: ast:: edit_in_place:: Indent ;
1213use syntax:: ast:: syntax_factory:: SyntaxFactory ;
@@ -79,12 +80,20 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
7980
8081 let make = SyntaxFactory :: with_mappings ( ) ;
8182
82- let module = ctx. sema . scope ( expr. syntax ( ) ) ?. module ( ) ;
83+ let scope = ctx. sema . scope ( expr. syntax ( ) ) ?;
84+ let module = scope. module ( ) ;
85+ let self_ty = if ctx. config . prefer_self_ty {
86+ scope
87+ . containing_function ( )
88+ . and_then ( |function| function. as_assoc_item ( ctx. db ( ) ) ?. implementing_ty ( ctx. db ( ) ) )
89+ } else {
90+ None
91+ } ;
8392 let ( mut missing_pats, is_non_exhaustive, has_hidden_variants) : (
8493 Peekable < Box < dyn Iterator < Item = ( ast:: Pat , bool ) > > > ,
8594 bool ,
8695 bool ,
87- ) = if let Some ( enum_def) = resolve_enum_def ( & ctx. sema , & expr) {
96+ ) = if let Some ( enum_def) = resolve_enum_def ( & ctx. sema , & expr, self_ty . as_ref ( ) ) {
8897 let is_non_exhaustive = enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ;
8998
9099 let variants = enum_def. variants ( ctx. db ( ) ) ;
@@ -102,16 +111,17 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
102111 } )
103112 . filter ( |( variant_pat, _) | is_variant_missing ( & top_lvl_pats, variant_pat) ) ;
104113
105- let option_enum = FamousDefs ( & ctx. sema , module. krate ( ) ) . core_option_Option ( ) . map ( lift_enum) ;
106- let missing_pats: Box < dyn Iterator < Item = _ > > = if Some ( enum_def) == option_enum {
114+ let option_enum = FamousDefs ( & ctx. sema , module. krate ( ) ) . core_option_Option ( ) ;
115+ let missing_pats: Box < dyn Iterator < Item = _ > > = if matches ! ( enum_def, ExtendedEnum :: Enum { enum_: e, .. } if Some ( e) == option_enum)
116+ {
107117 // Match `Some` variant first.
108118 cov_mark:: hit!( option_order) ;
109119 Box :: new ( missing_pats. rev ( ) )
110120 } else {
111121 Box :: new ( missing_pats)
112122 } ;
113123 ( missing_pats. peekable ( ) , is_non_exhaustive, has_hidden_variants)
114- } else if let Some ( enum_defs) = resolve_tuple_of_enum_def ( & ctx. sema , & expr) {
124+ } else if let Some ( enum_defs) = resolve_tuple_of_enum_def ( & ctx. sema , & expr, self_ty . as_ref ( ) ) {
115125 let is_non_exhaustive =
116126 enum_defs. iter ( ) . any ( |enum_def| enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ) ;
117127
@@ -159,7 +169,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
159169 is_non_exhaustive,
160170 has_hidden_variants,
161171 )
162- } else if let Some ( ( enum_def, len) ) = resolve_array_of_enum_def ( & ctx. sema , & expr) {
172+ } else if let Some ( ( enum_def, len) ) =
173+ resolve_array_of_enum_def ( & ctx. sema , & expr, self_ty. as_ref ( ) )
174+ {
163175 let is_non_exhaustive = enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ;
164176 let variants = enum_def. variants ( ctx. db ( ) ) ;
165177
@@ -373,66 +385,81 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
373385 }
374386}
375387
376- #[ derive( Eq , PartialEq , Clone , Copy ) ]
388+ #[ derive( Eq , PartialEq , Clone ) ]
377389enum ExtendedEnum {
378390 Bool ,
379- Enum ( hir:: Enum ) ,
391+ Enum { enum_ : hir:: Enum , use_self : bool } ,
380392}
381393
382394#[ derive( Eq , PartialEq , Clone , Copy , Debug ) ]
383395enum ExtendedVariant {
384396 True ,
385397 False ,
386- Variant ( hir:: Variant ) ,
398+ Variant { variant : hir:: Variant , use_self : bool } ,
387399}
388400
389401impl ExtendedVariant {
390402 fn should_be_hidden ( self , db : & RootDatabase , krate : Crate ) -> bool {
391403 match self {
392- ExtendedVariant :: Variant ( var) => {
404+ ExtendedVariant :: Variant { variant : var, .. } => {
393405 var. attrs ( db) . has_doc_hidden ( ) && var. module ( db) . krate ( ) != krate
394406 }
395407 _ => false ,
396408 }
397409 }
398410}
399411
400- fn lift_enum ( e : hir:: Enum ) -> ExtendedEnum {
401- ExtendedEnum :: Enum ( e)
402- }
403-
404412impl ExtendedEnum {
405- fn is_non_exhaustive ( self , db : & RootDatabase , krate : Crate ) -> bool {
413+ fn enum_ (
414+ db : & RootDatabase ,
415+ enum_ : hir:: Enum ,
416+ enum_ty : & hir:: Type ,
417+ self_ty : Option < & hir:: Type > ,
418+ ) -> Self {
419+ ExtendedEnum :: Enum {
420+ enum_,
421+ use_self : self_ty. is_some_and ( |self_ty| self_ty. could_unify_with_deeply ( db, enum_ty) ) ,
422+ }
423+ }
424+
425+ fn is_non_exhaustive ( & self , db : & RootDatabase , krate : Crate ) -> bool {
406426 match self {
407- ExtendedEnum :: Enum ( e ) => {
427+ ExtendedEnum :: Enum { enum_ : e , .. } => {
408428 e. attrs ( db) . by_key ( sym:: non_exhaustive) . exists ( ) && e. module ( db) . krate ( ) != krate
409429 }
410430 _ => false ,
411431 }
412432 }
413433
414- fn variants ( self , db : & RootDatabase ) -> Vec < ExtendedVariant > {
415- match self {
416- ExtendedEnum :: Enum ( e) => {
417- e. variants ( db) . into_iter ( ) . map ( ExtendedVariant :: Variant ) . collect :: < Vec < _ > > ( )
418- }
434+ fn variants ( & self , db : & RootDatabase ) -> Vec < ExtendedVariant > {
435+ match * self {
436+ ExtendedEnum :: Enum { enum_ : e, use_self } => e
437+ . variants ( db)
438+ . into_iter ( )
439+ . map ( |variant| ExtendedVariant :: Variant { variant, use_self } )
440+ . collect :: < Vec < _ > > ( ) ,
419441 ExtendedEnum :: Bool => {
420442 Vec :: < ExtendedVariant > :: from ( [ ExtendedVariant :: True , ExtendedVariant :: False ] )
421443 }
422444 }
423445 }
424446}
425447
426- fn resolve_enum_def ( sema : & Semantics < ' _ , RootDatabase > , expr : & ast:: Expr ) -> Option < ExtendedEnum > {
448+ fn resolve_enum_def (
449+ sema : & Semantics < ' _ , RootDatabase > ,
450+ expr : & ast:: Expr ,
451+ self_ty : Option < & hir:: Type > ,
452+ ) -> Option < ExtendedEnum > {
427453 sema. type_of_expr ( expr) ?. adjusted ( ) . autoderef ( sema. db ) . find_map ( |ty| match ty. as_adt ( ) {
428- Some ( Adt :: Enum ( e) ) => Some ( ExtendedEnum :: Enum ( e ) ) ,
454+ Some ( Adt :: Enum ( e) ) => Some ( ExtendedEnum :: enum_ ( sema . db , e , & ty , self_ty ) ) ,
429455 _ => ty. is_bool ( ) . then_some ( ExtendedEnum :: Bool ) ,
430456 } )
431457}
432458
433459fn resolve_tuple_of_enum_def (
434460 sema : & Semantics < ' _ , RootDatabase > ,
435461 expr : & ast:: Expr ,
462+ self_ty : Option < & hir:: Type > ,
436463) -> Option < Vec < ExtendedEnum > > {
437464 sema. type_of_expr ( expr) ?
438465 . adjusted ( )
@@ -441,7 +468,7 @@ fn resolve_tuple_of_enum_def(
441468 . map ( |ty| {
442469 ty. autoderef ( sema. db ) . find_map ( |ty| {
443470 match ty. as_adt ( ) {
444- Some ( Adt :: Enum ( e) ) => Some ( lift_enum ( e ) ) ,
471+ Some ( Adt :: Enum ( e) ) => Some ( ExtendedEnum :: enum_ ( sema . db , e , & ty , self_ty ) ) ,
445472 // For now we only handle expansion for a tuple of enums. Here
446473 // we map non-enum items to None and rely on `collect` to
447474 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
@@ -456,10 +483,11 @@ fn resolve_tuple_of_enum_def(
456483fn resolve_array_of_enum_def (
457484 sema : & Semantics < ' _ , RootDatabase > ,
458485 expr : & ast:: Expr ,
486+ self_ty : Option < & hir:: Type > ,
459487) -> Option < ( ExtendedEnum , usize ) > {
460488 sema. type_of_expr ( expr) ?. adjusted ( ) . as_array ( sema. db ) . and_then ( |( ty, len) | {
461489 ty. autoderef ( sema. db ) . find_map ( |ty| match ty. as_adt ( ) {
462- Some ( Adt :: Enum ( e) ) => Some ( ( lift_enum ( e ) , len) ) ,
490+ Some ( Adt :: Enum ( e) ) => Some ( ( ExtendedEnum :: enum_ ( sema . db , e , & ty , self_ty ) , len) ) ,
463491 _ => ty. is_bool ( ) . then_some ( ( ExtendedEnum :: Bool , len) ) ,
464492 } )
465493 } )
@@ -474,9 +502,21 @@ fn build_pat(
474502) -> Option < ast:: Pat > {
475503 let db = ctx. db ( ) ;
476504 match var {
477- ExtendedVariant :: Variant ( var) => {
505+ ExtendedVariant :: Variant { variant : var, use_self } => {
478506 let edition = module. krate ( ) . edition ( db) ;
479- let path = mod_path_to_ast ( & module. find_path ( db, ModuleDef :: from ( var) , cfg) ?, edition) ;
507+ let path = if use_self {
508+ make:: path_from_segments (
509+ [
510+ make:: path_segment ( make:: name_ref_self_ty ( ) ) ,
511+ make:: path_segment ( make:: name_ref (
512+ & var. name ( db) . display ( db, edition) . to_smolstr ( ) ,
513+ ) ) ,
514+ ] ,
515+ false ,
516+ )
517+ } else {
518+ mod_path_to_ast ( & module. find_path ( db, ModuleDef :: from ( var) , cfg) ?, edition)
519+ } ;
480520 let fields = var. fields ( db) ;
481521 let pat: ast:: Pat = match var. kind ( db) {
482522 hir:: StructKind :: Tuple => {
@@ -509,8 +549,10 @@ fn build_pat(
509549
510550#[ cfg( test) ]
511551mod tests {
552+ use crate :: AssistConfig ;
512553 use crate :: tests:: {
513- check_assist, check_assist_not_applicable, check_assist_target, check_assist_unresolved,
554+ TEST_CONFIG , check_assist, check_assist_not_applicable, check_assist_target,
555+ check_assist_unresolved, check_assist_with_config,
514556 } ;
515557
516558 use super :: add_missing_match_arms;
@@ -2095,4 +2137,111 @@ fn f() {
20952137"# ,
20962138 ) ;
20972139 }
2140+
2141+ #[ test]
2142+ fn prefer_self ( ) {
2143+ check_assist_with_config (
2144+ add_missing_match_arms,
2145+ AssistConfig { prefer_self_ty : true , ..TEST_CONFIG } ,
2146+ r#"
2147+ enum Foo {
2148+ Bar,
2149+ Baz,
2150+ }
2151+
2152+ impl Foo {
2153+ fn qux(&self) {
2154+ match self {
2155+ $0_ => {}
2156+ }
2157+ }
2158+ }
2159+ "# ,
2160+ r#"
2161+ enum Foo {
2162+ Bar,
2163+ Baz,
2164+ }
2165+
2166+ impl Foo {
2167+ fn qux(&self) {
2168+ match self {
2169+ Self::Bar => ${1:todo!()},
2170+ Self::Baz => ${2:todo!()},$0
2171+ }
2172+ }
2173+ }
2174+ "# ,
2175+ ) ;
2176+ }
2177+
2178+ #[ test]
2179+ fn prefer_self_with_generics ( ) {
2180+ check_assist_with_config (
2181+ add_missing_match_arms,
2182+ AssistConfig { prefer_self_ty : true , ..TEST_CONFIG } ,
2183+ r#"
2184+ enum Foo<T> {
2185+ Bar(T),
2186+ Baz,
2187+ }
2188+
2189+ impl<T> Foo<T> {
2190+ fn qux(&self) {
2191+ match self {
2192+ $0_ => {}
2193+ }
2194+ }
2195+ }
2196+ "# ,
2197+ r#"
2198+ enum Foo<T> {
2199+ Bar(T),
2200+ Baz,
2201+ }
2202+
2203+ impl<T> Foo<T> {
2204+ fn qux(&self) {
2205+ match self {
2206+ Self::Bar(${1:_}) => ${2:todo!()},
2207+ Self::Baz => ${3:todo!()},$0
2208+ }
2209+ }
2210+ }
2211+ "# ,
2212+ ) ;
2213+ check_assist_with_config (
2214+ add_missing_match_arms,
2215+ AssistConfig { prefer_self_ty : true , ..TEST_CONFIG } ,
2216+ r#"
2217+ enum Foo<T> {
2218+ Bar(T),
2219+ Baz,
2220+ }
2221+
2222+ impl<T> Foo<T> {
2223+ fn qux(v: Foo<i32>) {
2224+ match v {
2225+ $0_ => {}
2226+ }
2227+ }
2228+ }
2229+ "# ,
2230+ r#"
2231+ enum Foo<T> {
2232+ Bar(T),
2233+ Baz,
2234+ }
2235+
2236+ impl<T> Foo<T> {
2237+ fn qux(v: Foo<i32>) {
2238+ match v {
2239+ Foo::Bar(${1:_}) => ${2:todo!()},
2240+ Foo::Baz => ${3:todo!()},$0
2241+ }
2242+ }
2243+ }
2244+ "# ,
2245+ ) ;
2246+ }
20982247}
0 commit comments