1
1
use std:: iter;
2
2
3
+ use either:: Either ;
3
4
use hir:: { Adt , HasSource , ModuleDef , Semantics } ;
4
5
use ide_db:: helpers:: { mod_path_to_ast, FamousDefs } ;
5
6
use ide_db:: RootDatabase ;
6
7
use itertools:: Itertools ;
7
8
use syntax:: ast:: { self , make, AstNode , MatchArm , NameOwner , Pat } ;
8
9
9
10
use crate :: {
10
- utils:: { does_pat_match_variant , render_snippet, Cursor } ,
11
+ utils:: { self , render_snippet, Cursor } ,
11
12
AssistContext , AssistId , AssistKind , Assists ,
12
13
} ;
13
14
@@ -48,6 +49,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
48
49
}
49
50
}
50
51
52
+ let top_lvl_pats: Vec < _ > = arms
53
+ . iter ( )
54
+ . filter_map ( ast:: MatchArm :: pat)
55
+ . flat_map ( |pat| match pat {
56
+ // Special casee OrPat as separate top-level pats
57
+ Pat :: OrPat ( or_pat) => Either :: Left ( or_pat. pats ( ) ) ,
58
+ _ => Either :: Right ( iter:: once ( pat) ) ,
59
+ } )
60
+ // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
61
+ . filter ( |pat| !matches ! ( pat, Pat :: WildcardPat ( _) ) )
62
+ . collect ( ) ;
63
+
51
64
let module = ctx. sema . scope ( expr. syntax ( ) ) . module ( ) ?;
52
65
53
66
let missing_arms: Vec < MatchArm > = if let Some ( enum_def) = resolve_enum_def ( & ctx. sema , & expr) {
@@ -56,7 +69,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
56
69
let mut variants = variants
57
70
. into_iter ( )
58
71
. filter_map ( |variant| build_pat ( ctx. db ( ) , module, variant) )
59
- . filter ( |variant_pat| is_variant_missing ( & mut arms , variant_pat) )
72
+ . filter ( |variant_pat| is_variant_missing ( & top_lvl_pats , variant_pat) )
60
73
. map ( |pat| make:: match_arm ( iter:: once ( pat) , make:: expr_empty_block ( ) ) )
61
74
. collect :: < Vec < _ > > ( ) ;
62
75
if Some ( enum_def) == FamousDefs ( & ctx. sema , Some ( module. krate ( ) ) ) . core_option_Option ( ) {
@@ -66,11 +79,6 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
66
79
}
67
80
variants
68
81
} else if let Some ( enum_defs) = resolve_tuple_of_enum_def ( & ctx. sema , & expr) {
69
- // Partial fill not currently supported for tuple of enums.
70
- if !arms. is_empty ( ) {
71
- return None ;
72
- }
73
-
74
82
// When calculating the match arms for a tuple of enums, we want
75
83
// to create a match arm for each possible combination of enum
76
84
// values. The `multi_cartesian_product` method transforms
@@ -85,7 +93,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
85
93
variants. into_iter ( ) . filter_map ( |variant| build_pat ( ctx. db ( ) , module, variant) ) ;
86
94
ast:: Pat :: from ( make:: tuple_pat ( patterns) )
87
95
} )
88
- . filter ( |variant_pat| is_variant_missing ( & mut arms , variant_pat) )
96
+ . filter ( |variant_pat| is_variant_missing ( & top_lvl_pats , variant_pat) )
89
97
. map ( |pat| make:: match_arm ( iter:: once ( pat) , make:: expr_empty_block ( ) ) )
90
98
. collect ( )
91
99
} else {
@@ -128,16 +136,19 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
128
136
)
129
137
}
130
138
131
- fn is_variant_missing ( existing_arms : & mut Vec < MatchArm > , var : & Pat ) -> bool {
132
- existing_arms. iter ( ) . filter_map ( |arm| arm. pat ( ) ) . all ( |pat| {
133
- // Special casee OrPat as separate top-level pats
134
- let top_level_pats: Vec < Pat > = match pat {
135
- Pat :: OrPat ( pats) => pats. pats ( ) . collect :: < Vec < _ > > ( ) ,
136
- _ => vec ! [ pat] ,
137
- } ;
139
+ fn is_variant_missing ( existing_pats : & [ Pat ] , var : & Pat ) -> bool {
140
+ !existing_pats. iter ( ) . any ( |pat| does_pat_match_variant ( pat, var) )
141
+ }
138
142
139
- !top_level_pats. iter ( ) . any ( |pat| does_pat_match_variant ( pat, var) )
140
- } )
143
+ // Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
144
+ fn does_pat_match_variant ( pat : & Pat , var : & Pat ) -> bool {
145
+ match ( pat, var) {
146
+ ( Pat :: WildcardPat ( _) , _) => true ,
147
+ ( Pat :: TuplePat ( tpat) , Pat :: TuplePat ( tvar) ) => {
148
+ tpat. fields ( ) . zip ( tvar. fields ( ) ) . all ( |( p, v) | does_pat_match_variant ( & p, & v) )
149
+ }
150
+ _ => utils:: does_pat_match_variant ( pat, var) ,
151
+ }
141
152
}
142
153
143
154
fn resolve_enum_def ( sema : & Semantics < RootDatabase > , expr : & ast:: Expr ) -> Option < hir:: Enum > {
@@ -467,20 +478,81 @@ fn main() {
467
478
468
479
#[ test]
469
480
fn fill_match_arms_tuple_of_enum_partial ( ) {
470
- check_assist_not_applicable (
481
+ check_assist (
471
482
fill_match_arms,
472
483
r#"
473
- enum A { One, Two }
474
- enum B { One, Two }
484
+ enum A { One, Two }
485
+ enum B { One, Two }
475
486
476
- fn main() {
477
- let a = A::One;
478
- let b = B::One;
479
- match (a$0, b) {
480
- (A::Two, B::One) => {}
481
- }
482
- }
483
- "# ,
487
+ fn main() {
488
+ let a = A::One;
489
+ let b = B::One;
490
+ match (a$0, b) {
491
+ (A::Two, B::One) => {}
492
+ }
493
+ }
494
+ "# ,
495
+ r#"
496
+ enum A { One, Two }
497
+ enum B { One, Two }
498
+
499
+ fn main() {
500
+ let a = A::One;
501
+ let b = B::One;
502
+ match (a, b) {
503
+ (A::Two, B::One) => {}
504
+ $0(A::One, B::One) => {}
505
+ (A::One, B::Two) => {}
506
+ (A::Two, B::Two) => {}
507
+ }
508
+ }
509
+ "# ,
510
+ ) ;
511
+ }
512
+
513
+ #[ test]
514
+ fn fill_match_arms_tuple_of_enum_partial_with_wildcards ( ) {
515
+ let ra_fixture = r#"
516
+ fn main() {
517
+ let a = Some(1);
518
+ let b = Some(());
519
+ match (a$0, b) {
520
+ (Some(_), _) => {}
521
+ (None, Some(_)) => {}
522
+ }
523
+ }
524
+ "# ;
525
+ check_assist (
526
+ fill_match_arms,
527
+ & format ! ( "//- /main.rs crate:main deps:core{}{}" , ra_fixture, FamousDefs :: FIXTURE ) ,
528
+ r#"
529
+ fn main() {
530
+ let a = Some(1);
531
+ let b = Some(());
532
+ match (a, b) {
533
+ (Some(_), _) => {}
534
+ (None, Some(_)) => {}
535
+ $0(None, None) => {}
536
+ }
537
+ }
538
+ "# ,
539
+ ) ;
540
+ }
541
+
542
+ #[ test]
543
+ fn fill_match_arms_partial_with_deep_pattern ( ) {
544
+ // Fixme: cannot handle deep patterns
545
+ let ra_fixture = r#"
546
+ fn main() {
547
+ match $0Some(true) {
548
+ Some(true) => {}
549
+ None => {}
550
+ }
551
+ }
552
+ "# ;
553
+ check_assist_not_applicable (
554
+ fill_match_arms,
555
+ & format ! ( "//- /main.rs crate:main deps:core{}{}" , ra_fixture, FamousDefs :: FIXTURE ) ,
484
556
) ;
485
557
}
486
558
0 commit comments