1
1
use std:: iter:: { self , Peekable } ;
2
2
3
3
use either:: Either ;
4
- use hir:: { Adt , Crate , HasAttrs , ImportPathConfig , ModuleDef , Semantics , sym} ;
4
+ use hir:: { Adt , AsAssocItem , Crate , HasAttrs , ImportPathConfig , ModuleDef , Semantics , sym} ;
5
5
use ide_db:: RootDatabase ;
6
6
use ide_db:: assists:: ExprFillDefaultMode ;
7
7
use ide_db:: syntax_helpers:: suggest_name;
8
8
use ide_db:: { famous_defs:: FamousDefs , helpers:: mod_path_to_ast} ;
9
9
use itertools:: Itertools ;
10
+ use syntax:: ToSmolStr ;
10
11
use syntax:: ast:: edit:: IndentLevel ;
11
12
use syntax:: ast:: edit_in_place:: Indent ;
12
13
use syntax:: ast:: syntax_factory:: SyntaxFactory ;
@@ -79,12 +80,20 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
79
80
80
81
let make = SyntaxFactory :: with_mappings ( ) ;
81
82
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
+ } ;
83
92
let ( mut missing_pats, is_non_exhaustive, has_hidden_variants) : (
84
93
Peekable < Box < dyn Iterator < Item = ( ast:: Pat , bool ) > > > ,
85
94
bool ,
86
95
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 ( ) ) {
88
97
let is_non_exhaustive = enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ;
89
98
90
99
let variants = enum_def. variants ( ctx. db ( ) ) ;
@@ -102,16 +111,17 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
102
111
} )
103
112
. filter ( |( variant_pat, _) | is_variant_missing ( & top_lvl_pats, variant_pat) ) ;
104
113
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
+ {
107
117
// Match `Some` variant first.
108
118
cov_mark:: hit!( option_order) ;
109
119
Box :: new ( missing_pats. rev ( ) )
110
120
} else {
111
121
Box :: new ( missing_pats)
112
122
} ;
113
123
( 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 ( ) ) {
115
125
let is_non_exhaustive =
116
126
enum_defs. iter ( ) . any ( |enum_def| enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ) ;
117
127
@@ -159,7 +169,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
159
169
is_non_exhaustive,
160
170
has_hidden_variants,
161
171
)
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
+ {
163
175
let is_non_exhaustive = enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ;
164
176
let variants = enum_def. variants ( ctx. db ( ) ) ;
165
177
@@ -373,66 +385,81 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
373
385
}
374
386
}
375
387
376
- #[ derive( Eq , PartialEq , Clone , Copy ) ]
388
+ #[ derive( Eq , PartialEq , Clone ) ]
377
389
enum ExtendedEnum {
378
390
Bool ,
379
- Enum ( hir:: Enum ) ,
391
+ Enum { enum_ : hir:: Enum , use_self : bool } ,
380
392
}
381
393
382
394
#[ derive( Eq , PartialEq , Clone , Copy , Debug ) ]
383
395
enum ExtendedVariant {
384
396
True ,
385
397
False ,
386
- Variant ( hir:: Variant ) ,
398
+ Variant { variant : hir:: Variant , use_self : bool } ,
387
399
}
388
400
389
401
impl ExtendedVariant {
390
402
fn should_be_hidden ( self , db : & RootDatabase , krate : Crate ) -> bool {
391
403
match self {
392
- ExtendedVariant :: Variant ( var) => {
404
+ ExtendedVariant :: Variant { variant : var, .. } => {
393
405
var. attrs ( db) . has_doc_hidden ( ) && var. module ( db) . krate ( ) != krate
394
406
}
395
407
_ => false ,
396
408
}
397
409
}
398
410
}
399
411
400
- fn lift_enum ( e : hir:: Enum ) -> ExtendedEnum {
401
- ExtendedEnum :: Enum ( e)
402
- }
403
-
404
412
impl 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 {
406
426
match self {
407
- ExtendedEnum :: Enum ( e ) => {
427
+ ExtendedEnum :: Enum { enum_ : e , .. } => {
408
428
e. attrs ( db) . by_key ( sym:: non_exhaustive) . exists ( ) && e. module ( db) . krate ( ) != krate
409
429
}
410
430
_ => false ,
411
431
}
412
432
}
413
433
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 < _ > > ( ) ,
419
441
ExtendedEnum :: Bool => {
420
442
Vec :: < ExtendedVariant > :: from ( [ ExtendedVariant :: True , ExtendedVariant :: False ] )
421
443
}
422
444
}
423
445
}
424
446
}
425
447
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 > {
427
453
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 ) ) ,
429
455
_ => ty. is_bool ( ) . then_some ( ExtendedEnum :: Bool ) ,
430
456
} )
431
457
}
432
458
433
459
fn resolve_tuple_of_enum_def (
434
460
sema : & Semantics < ' _ , RootDatabase > ,
435
461
expr : & ast:: Expr ,
462
+ self_ty : Option < & hir:: Type > ,
436
463
) -> Option < Vec < ExtendedEnum > > {
437
464
sema. type_of_expr ( expr) ?
438
465
. adjusted ( )
@@ -441,7 +468,7 @@ fn resolve_tuple_of_enum_def(
441
468
. map ( |ty| {
442
469
ty. autoderef ( sema. db ) . find_map ( |ty| {
443
470
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 ) ) ,
445
472
// For now we only handle expansion for a tuple of enums. Here
446
473
// we map non-enum items to None and rely on `collect` to
447
474
// convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
@@ -456,10 +483,11 @@ fn resolve_tuple_of_enum_def(
456
483
fn resolve_array_of_enum_def (
457
484
sema : & Semantics < ' _ , RootDatabase > ,
458
485
expr : & ast:: Expr ,
486
+ self_ty : Option < & hir:: Type > ,
459
487
) -> Option < ( ExtendedEnum , usize ) > {
460
488
sema. type_of_expr ( expr) ?. adjusted ( ) . as_array ( sema. db ) . and_then ( |( ty, len) | {
461
489
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) ) ,
463
491
_ => ty. is_bool ( ) . then_some ( ( ExtendedEnum :: Bool , len) ) ,
464
492
} )
465
493
} )
@@ -474,9 +502,21 @@ fn build_pat(
474
502
) -> Option < ast:: Pat > {
475
503
let db = ctx. db ( ) ;
476
504
match var {
477
- ExtendedVariant :: Variant ( var) => {
505
+ ExtendedVariant :: Variant { variant : var, use_self } => {
478
506
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
+ } ;
480
520
let fields = var. fields ( db) ;
481
521
let pat: ast:: Pat = match var. kind ( db) {
482
522
hir:: StructKind :: Tuple => {
@@ -509,8 +549,10 @@ fn build_pat(
509
549
510
550
#[ cfg( test) ]
511
551
mod tests {
552
+ use crate :: AssistConfig ;
512
553
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,
514
556
} ;
515
557
516
558
use super :: add_missing_match_arms;
@@ -2095,4 +2137,111 @@ fn f() {
2095
2137
"# ,
2096
2138
) ;
2097
2139
}
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
+ }
2098
2247
}
0 commit comments