3
3
use std:: iter;
4
4
5
5
use hir:: { Adt , HasSource , Semantics } ;
6
+ use itertools:: Itertools ;
6
7
use ra_ide_db:: RootDatabase ;
7
8
8
9
use crate :: { Assist , AssistCtx , AssistId } ;
@@ -39,13 +40,6 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
39
40
let match_arm_list = match_expr. match_arm_list ( ) ?;
40
41
41
42
let expr = match_expr. expr ( ) ?;
42
- let enum_def = resolve_enum_def ( & ctx. sema , & expr) ?;
43
- let module = ctx. sema . scope ( expr. syntax ( ) ) . module ( ) ?;
44
-
45
- let variants = enum_def. variants ( ctx. db ) ;
46
- if variants. is_empty ( ) {
47
- return None ;
48
- }
49
43
50
44
let mut arms: Vec < MatchArm > = match_arm_list. arms ( ) . collect ( ) ;
51
45
if arms. len ( ) == 1 {
@@ -54,13 +48,49 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
54
48
}
55
49
}
56
50
57
- let db = ctx. db ;
58
- let missing_arms: Vec < MatchArm > = variants
59
- . into_iter ( )
60
- . filter_map ( |variant| build_pat ( db, module, variant) )
61
- . filter ( |variant_pat| is_variant_missing ( & mut arms, variant_pat) )
62
- . map ( |pat| make:: match_arm ( iter:: once ( pat) , make:: expr_unit ( ) ) )
63
- . collect ( ) ;
51
+ let module = ctx. sema . scope ( expr. syntax ( ) ) . module ( ) ?;
52
+
53
+ let missing_arms: Vec < MatchArm > = if let Some ( enum_def) = resolve_enum_def ( & ctx. sema , & expr) {
54
+ let variants = enum_def. variants ( ctx. db ) ;
55
+
56
+ variants
57
+ . into_iter ( )
58
+ . filter_map ( |variant| build_pat ( ctx. db , module, variant) )
59
+ . filter ( |variant_pat| is_variant_missing ( & mut arms, variant_pat) )
60
+ . map ( |pat| make:: match_arm ( iter:: once ( pat) , make:: expr_unit ( ) ) )
61
+ . collect ( )
62
+ } else if let Some ( enum_defs) = resolve_tuple_of_enum_def ( & ctx. sema , & expr) {
63
+ // Partial fill not currently supported for tuple of enums.
64
+ if !arms. is_empty ( ) {
65
+ return None ;
66
+ }
67
+
68
+ // We do not currently support filling match arms for a tuple
69
+ // containing a single enum.
70
+ if enum_defs. len ( ) < 2 {
71
+ return None ;
72
+ }
73
+
74
+ // When calculating the match arms for a tuple of enums, we want
75
+ // to create a match arm for each possible combination of enum
76
+ // values. The `multi_cartesian_product` method transforms
77
+ // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
78
+ // where each tuple represents a proposed match arm.
79
+ enum_defs
80
+ . into_iter ( )
81
+ . map ( |enum_def| enum_def. variants ( ctx. db ) )
82
+ . multi_cartesian_product ( )
83
+ . map ( |variants| {
84
+ let patterns =
85
+ variants. into_iter ( ) . filter_map ( |variant| build_pat ( ctx. db , module, variant) ) ;
86
+ ast:: Pat :: from ( make:: tuple_pat ( patterns) )
87
+ } )
88
+ . filter ( |variant_pat| is_variant_missing ( & mut arms, variant_pat) )
89
+ . map ( |pat| make:: match_arm ( iter:: once ( pat) , make:: expr_unit ( ) ) )
90
+ . collect ( )
91
+ } else {
92
+ return None ;
93
+ } ;
64
94
65
95
if missing_arms. is_empty ( ) {
66
96
return None ;
@@ -104,6 +134,25 @@ fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
104
134
} )
105
135
}
106
136
137
+ fn resolve_tuple_of_enum_def (
138
+ sema : & Semantics < RootDatabase > ,
139
+ expr : & ast:: Expr ,
140
+ ) -> Option < Vec < hir:: Enum > > {
141
+ sema. type_of_expr ( & expr) ?
142
+ . tuple_fields ( sema. db )
143
+ . iter ( )
144
+ . map ( |ty| {
145
+ ty. autoderef ( sema. db ) . find_map ( |ty| match ty. as_adt ( ) {
146
+ Some ( Adt :: Enum ( e) ) => Some ( e) ,
147
+ // For now we only handle expansion for a tuple of enums. Here
148
+ // we map non-enum items to None and rely on `collect` to
149
+ // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
150
+ _ => None ,
151
+ } )
152
+ } )
153
+ . collect ( )
154
+ }
155
+
107
156
fn build_pat ( db : & RootDatabase , module : hir:: Module , var : hir:: EnumVariant ) -> Option < ast:: Pat > {
108
157
let path = crate :: ast_transform:: path_to_ast ( module. find_use_path ( db, var. into ( ) ) ?) ;
109
158
@@ -151,6 +200,21 @@ mod tests {
151
200
) ;
152
201
}
153
202
203
+ #[ test]
204
+ fn tuple_of_non_enum ( ) {
205
+ // for now this case is not handled, although it potentially could be
206
+ // in the future
207
+ check_assist_not_applicable (
208
+ fill_match_arms,
209
+ r#"
210
+ fn main() {
211
+ match (0, false)<|> {
212
+ }
213
+ }
214
+ "# ,
215
+ ) ;
216
+ }
217
+
154
218
#[ test]
155
219
fn partial_fill_record_tuple ( ) {
156
220
check_assist (
@@ -307,6 +371,169 @@ mod tests {
307
371
) ;
308
372
}
309
373
374
+ #[ test]
375
+ fn fill_match_arms_tuple_of_enum ( ) {
376
+ check_assist (
377
+ fill_match_arms,
378
+ r#"
379
+ enum A {
380
+ One,
381
+ Two,
382
+ }
383
+ enum B {
384
+ One,
385
+ Two,
386
+ }
387
+
388
+ fn main() {
389
+ let a = A::One;
390
+ let b = B::One;
391
+ match (a<|>, b) {}
392
+ }
393
+ "# ,
394
+ r#"
395
+ enum A {
396
+ One,
397
+ Two,
398
+ }
399
+ enum B {
400
+ One,
401
+ Two,
402
+ }
403
+
404
+ fn main() {
405
+ let a = A::One;
406
+ let b = B::One;
407
+ match <|>(a, b) {
408
+ (A::One, B::One) => (),
409
+ (A::One, B::Two) => (),
410
+ (A::Two, B::One) => (),
411
+ (A::Two, B::Two) => (),
412
+ }
413
+ }
414
+ "# ,
415
+ ) ;
416
+ }
417
+
418
+ #[ test]
419
+ fn fill_match_arms_tuple_of_enum_ref ( ) {
420
+ check_assist (
421
+ fill_match_arms,
422
+ r#"
423
+ enum A {
424
+ One,
425
+ Two,
426
+ }
427
+ enum B {
428
+ One,
429
+ Two,
430
+ }
431
+
432
+ fn main() {
433
+ let a = A::One;
434
+ let b = B::One;
435
+ match (&a<|>, &b) {}
436
+ }
437
+ "# ,
438
+ r#"
439
+ enum A {
440
+ One,
441
+ Two,
442
+ }
443
+ enum B {
444
+ One,
445
+ Two,
446
+ }
447
+
448
+ fn main() {
449
+ let a = A::One;
450
+ let b = B::One;
451
+ match <|>(&a, &b) {
452
+ (A::One, B::One) => (),
453
+ (A::One, B::Two) => (),
454
+ (A::Two, B::One) => (),
455
+ (A::Two, B::Two) => (),
456
+ }
457
+ }
458
+ "# ,
459
+ ) ;
460
+ }
461
+
462
+ #[ test]
463
+ fn fill_match_arms_tuple_of_enum_partial ( ) {
464
+ check_assist_not_applicable (
465
+ fill_match_arms,
466
+ r#"
467
+ enum A {
468
+ One,
469
+ Two,
470
+ }
471
+ enum B {
472
+ One,
473
+ Two,
474
+ }
475
+
476
+ fn main() {
477
+ let a = A::One;
478
+ let b = B::One;
479
+ match (a<|>, b) {
480
+ (A::Two, B::One) => (),
481
+ }
482
+ }
483
+ "# ,
484
+ ) ;
485
+ }
486
+
487
+ #[ test]
488
+ fn fill_match_arms_tuple_of_enum_not_applicable ( ) {
489
+ check_assist_not_applicable (
490
+ fill_match_arms,
491
+ r#"
492
+ enum A {
493
+ One,
494
+ Two,
495
+ }
496
+ enum B {
497
+ One,
498
+ Two,
499
+ }
500
+
501
+ fn main() {
502
+ let a = A::One;
503
+ let b = B::One;
504
+ match (a<|>, b) {
505
+ (A::Two, B::One) => (),
506
+ (A::One, B::One) => (),
507
+ (A::One, B::Two) => (),
508
+ (A::Two, B::Two) => (),
509
+ }
510
+ }
511
+ "# ,
512
+ ) ;
513
+ }
514
+
515
+ #[ test]
516
+ fn fill_match_arms_single_element_tuple_of_enum ( ) {
517
+ // For now we don't hande the case of a single element tuple, but
518
+ // we could handle this in the future if `make::tuple_pat` allowed
519
+ // creating a tuple with a single pattern.
520
+ check_assist_not_applicable (
521
+ fill_match_arms,
522
+ r#"
523
+ enum A {
524
+ One,
525
+ Two,
526
+ }
527
+
528
+ fn main() {
529
+ let a = A::One;
530
+ match (a<|>, ) {
531
+ }
532
+ }
533
+ "# ,
534
+ ) ;
535
+ }
536
+
310
537
#[ test]
311
538
fn test_fill_match_arm_refs ( ) {
312
539
check_assist (
0 commit comments