@@ -8,10 +8,7 @@ use itertools::Itertools;
88use syntax:: ast:: edit_in_place:: Removable ;
99use syntax:: ast:: { self , make, AstNode , HasName , MatchArmList , MatchExpr , Pat } ;
1010
11- use crate :: {
12- utils:: { self , render_snippet, Cursor } ,
13- AssistContext , AssistId , AssistKind , Assists ,
14- } ;
11+ use crate :: { utils, AssistContext , AssistId , AssistKind , Assists } ;
1512
1613// Assist: add_missing_match_arms
1714//
@@ -75,14 +72,18 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
7572 . collect ( ) ;
7673
7774 let module = ctx. sema . scope ( expr. syntax ( ) ) ?. module ( ) ;
78- let ( mut missing_pats, is_non_exhaustive) : (
75+ let ( mut missing_pats, is_non_exhaustive, has_hidden_variants ) : (
7976 Peekable < Box < dyn Iterator < Item = ( ast:: Pat , bool ) > > > ,
8077 bool ,
78+ bool ,
8179 ) = if let Some ( enum_def) = resolve_enum_def ( & ctx. sema , & expr) {
8280 let is_non_exhaustive = enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ;
8381
8482 let variants = enum_def. variants ( ctx. db ( ) ) ;
8583
84+ let has_hidden_variants =
85+ variants. iter ( ) . any ( |variant| variant. should_be_hidden ( ctx. db ( ) , module. krate ( ) ) ) ;
86+
8687 let missing_pats = variants
8788 . into_iter ( )
8889 . filter_map ( |variant| {
@@ -101,7 +102,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
101102 } else {
102103 Box :: new ( missing_pats)
103104 } ;
104- ( missing_pats. peekable ( ) , is_non_exhaustive)
105+ ( missing_pats. peekable ( ) , is_non_exhaustive, has_hidden_variants )
105106 } else if let Some ( enum_defs) = resolve_tuple_of_enum_def ( & ctx. sema , & expr) {
106107 let is_non_exhaustive =
107108 enum_defs. iter ( ) . any ( |enum_def| enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ) ;
@@ -124,6 +125,12 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
124125 if n_arms > 256 {
125126 return None ;
126127 }
128+
129+ let has_hidden_variants = variants_of_enums
130+ . iter ( )
131+ . flatten ( )
132+ . any ( |variant| variant. should_be_hidden ( ctx. db ( ) , module. krate ( ) ) ) ;
133+
127134 let missing_pats = variants_of_enums
128135 . into_iter ( )
129136 . multi_cartesian_product ( )
@@ -139,7 +146,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
139146 ( ast:: Pat :: from ( make:: tuple_pat ( patterns) ) , is_hidden)
140147 } )
141148 . filter ( |( variant_pat, _) | is_variant_missing ( & top_lvl_pats, variant_pat) ) ;
142- ( ( Box :: new ( missing_pats) as Box < dyn Iterator < Item = _ > > ) . peekable ( ) , is_non_exhaustive)
149+ (
150+ ( Box :: new ( missing_pats) as Box < dyn Iterator < Item = _ > > ) . peekable ( ) ,
151+ is_non_exhaustive,
152+ has_hidden_variants,
153+ )
143154 } else if let Some ( ( enum_def, len) ) = resolve_array_of_enum_def ( & ctx. sema , & expr) {
144155 let is_non_exhaustive = enum_def. is_non_exhaustive ( ctx. db ( ) , module. krate ( ) ) ;
145156 let variants = enum_def. variants ( ctx. db ( ) ) ;
@@ -148,6 +159,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
148159 return None ;
149160 }
150161
162+ let has_hidden_variants =
163+ variants. iter ( ) . any ( |variant| variant. should_be_hidden ( ctx. db ( ) , module. krate ( ) ) ) ;
164+
151165 let variants_of_enums = vec ! [ variants; len] ;
152166
153167 let missing_pats = variants_of_enums
@@ -164,28 +178,42 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
164178 ( ast:: Pat :: from ( make:: slice_pat ( patterns) ) , is_hidden)
165179 } )
166180 . filter ( |( variant_pat, _) | is_variant_missing ( & top_lvl_pats, variant_pat) ) ;
167- ( ( Box :: new ( missing_pats) as Box < dyn Iterator < Item = _ > > ) . peekable ( ) , is_non_exhaustive)
181+ (
182+ ( Box :: new ( missing_pats) as Box < dyn Iterator < Item = _ > > ) . peekable ( ) ,
183+ is_non_exhaustive,
184+ has_hidden_variants,
185+ )
168186 } else {
169187 return None ;
170188 } ;
171189
172190 let mut needs_catch_all_arm = is_non_exhaustive && !has_catch_all_arm;
173191
174- if !needs_catch_all_arm && missing_pats. peek ( ) . is_none ( ) {
192+ if !needs_catch_all_arm
193+ && ( ( has_hidden_variants && has_catch_all_arm) || missing_pats. peek ( ) . is_none ( ) )
194+ {
175195 return None ;
176196 }
177197
178198 acc. add (
179199 AssistId ( "add_missing_match_arms" , AssistKind :: QuickFix ) ,
180200 "Fill match arms" ,
181201 target_range,
182- |builder | {
202+ |edit | {
183203 let new_match_arm_list = match_arm_list. clone_for_update ( ) ;
204+
205+ // having any hidden variants means that we need a catch-all arm
206+ needs_catch_all_arm |= has_hidden_variants;
207+
184208 let missing_arms = missing_pats
185- . map ( |( pat, hidden) | {
186- ( make:: match_arm ( iter:: once ( pat) , None , make:: ext:: expr_todo ( ) ) , hidden)
209+ . filter ( |( _, hidden) | {
210+ // filter out hidden patterns because they're handled by the catch-all arm
211+ !hidden
187212 } )
188- . map ( |( it, hidden) | ( it. clone_for_update ( ) , hidden) ) ;
213+ . map ( |( pat, _) | {
214+ make:: match_arm ( iter:: once ( pat) , None , make:: ext:: expr_todo ( ) )
215+ . clone_for_update ( )
216+ } ) ;
189217
190218 let catch_all_arm = new_match_arm_list
191219 . arms ( )
@@ -204,15 +232,13 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
204232 cov_mark:: hit!( add_missing_match_arms_empty_expr) ;
205233 }
206234 }
235+
207236 let mut first_new_arm = None ;
208- for ( arm, hidden) in missing_arms {
209- if hidden {
210- needs_catch_all_arm = !has_catch_all_arm;
211- } else {
212- first_new_arm. get_or_insert_with ( || arm. clone ( ) ) ;
213- new_match_arm_list. add_arm ( arm) ;
214- }
237+ for arm in missing_arms {
238+ first_new_arm. get_or_insert_with ( || arm. clone ( ) ) ;
239+ new_match_arm_list. add_arm ( arm) ;
215240 }
241+
216242 if needs_catch_all_arm && !has_catch_all_arm {
217243 cov_mark:: hit!( added_wildcard_pattern) ;
218244 let arm = make:: match_arm (
@@ -225,24 +251,39 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
225251 new_match_arm_list. add_arm ( arm) ;
226252 }
227253
228- let old_range = ctx. sema . original_range ( match_arm_list. syntax ( ) ) . range ;
229- match ( first_new_arm, ctx. config . snippet_cap ) {
230- ( Some ( first_new_arm) , Some ( cap) ) => {
231- let extend_lifetime;
232- let cursor =
233- match first_new_arm. syntax ( ) . descendants ( ) . find_map ( ast:: WildcardPat :: cast)
234- {
235- Some ( it) => {
236- extend_lifetime = it. syntax ( ) . clone ( ) ;
237- Cursor :: Replace ( & extend_lifetime)
238- }
239- None => Cursor :: Before ( first_new_arm. syntax ( ) ) ,
240- } ;
241- let snippet = render_snippet ( cap, new_match_arm_list. syntax ( ) , cursor) ;
242- builder. replace_snippet ( cap, old_range, snippet) ;
254+ if let ( Some ( first_new_arm) , Some ( cap) ) = ( first_new_arm, ctx. config . snippet_cap ) {
255+ match first_new_arm. syntax ( ) . descendants ( ) . find_map ( ast:: WildcardPat :: cast) {
256+ Some ( it) => edit. add_placeholder_snippet ( cap, it) ,
257+ None => edit. add_tabstop_before ( cap, first_new_arm) ,
243258 }
244- _ => builder. replace ( old_range, new_match_arm_list. to_string ( ) ) ,
245259 }
260+
261+ // FIXME: Hack for mutable syntax trees not having great support for macros
262+ // Just replace the element that the original range came from
263+ let old_place = {
264+ // Find the original element
265+ let old_file_range = ctx. sema . original_range ( match_arm_list. syntax ( ) ) ;
266+ let file = ctx. sema . parse ( old_file_range. file_id ) ;
267+ let old_place = file. syntax ( ) . covering_element ( old_file_range. range ) ;
268+
269+ // Make `old_place` mut
270+ match old_place {
271+ syntax:: SyntaxElement :: Node ( it) => {
272+ syntax:: SyntaxElement :: from ( edit. make_syntax_mut ( it) )
273+ }
274+ syntax:: SyntaxElement :: Token ( it) => {
275+ // Don't have a way to make tokens mut, so instead make the parent mut
276+ // and find the token again
277+ let parent = edit. make_syntax_mut ( it. parent ( ) . unwrap ( ) ) ;
278+ let mut_token =
279+ parent. covering_element ( it. text_range ( ) ) . into_token ( ) . unwrap ( ) ;
280+
281+ syntax:: SyntaxElement :: from ( mut_token)
282+ }
283+ }
284+ } ;
285+
286+ syntax:: ted:: replace ( old_place, new_match_arm_list. syntax ( ) ) ;
246287 } ,
247288 )
248289}
@@ -1621,10 +1662,9 @@ pub enum E { #[doc(hidden)] A, }
16211662 ) ;
16221663 }
16231664
1624- // FIXME: I don't think the assist should be applicable in this case
16251665 #[ test]
16261666 fn does_not_fill_wildcard_with_wildcard ( ) {
1627- check_assist (
1667+ check_assist_not_applicable (
16281668 add_missing_match_arms,
16291669 r#"
16301670//- /main.rs crate:main deps:e
@@ -1635,13 +1675,6 @@ fn foo(t: ::e::E) {
16351675}
16361676//- /e.rs crate:e
16371677pub enum E { #[doc(hidden)] A, }
1638- "# ,
1639- r#"
1640- fn foo(t: ::e::E) {
1641- match t {
1642- _ => todo!(),
1643- }
1644- }
16451678"# ,
16461679 ) ;
16471680 }
@@ -1777,7 +1810,7 @@ fn foo(t: ::e::E, b: bool) {
17771810
17781811 #[ test]
17791812 fn does_not_fill_wildcard_with_partial_wildcard_and_wildcard ( ) {
1780- check_assist (
1813+ check_assist_not_applicable (
17811814 add_missing_match_arms,
17821815 r#"
17831816//- /main.rs crate:main deps:e
@@ -1789,14 +1822,6 @@ fn foo(t: ::e::E, b: bool) {
17891822}
17901823//- /e.rs crate:e
17911824pub enum E { #[doc(hidden)] A, }"# ,
1792- r#"
1793- fn foo(t: ::e::E, b: bool) {
1794- match t {
1795- _ if b => todo!(),
1796- _ => todo!(),
1797- }
1798- }
1799- "# ,
18001825 ) ;
18011826 }
18021827
0 commit comments