@@ -11,10 +11,11 @@ use ide_db::{
11
11
search:: FileReference ,
12
12
RootDatabase ,
13
13
} ;
14
+ use itertools:: Itertools ;
14
15
use rustc_hash:: FxHashSet ;
15
16
use syntax:: {
16
17
algo:: find_node_at_offset,
17
- ast:: { self , make, AstNode , NameOwner , VisibilityOwner } ,
18
+ ast:: { self , make, AstNode , GenericParamsOwner , NameOwner , TypeBoundsOwner , VisibilityOwner } ,
18
19
ted, SyntaxNode , T ,
19
20
} ;
20
21
@@ -100,12 +101,12 @@ pub(crate) fn extract_struct_from_enum_variant(
100
101
} ) ;
101
102
}
102
103
103
- let def = create_struct_def ( variant_name. clone ( ) , & field_list, enum_ast. visibility ( ) ) ;
104
+ let def = create_struct_def ( variant_name. clone ( ) , & field_list, & enum_ast) ;
104
105
let start_offset = & variant. parent_enum ( ) . syntax ( ) . clone ( ) ;
105
106
ted:: insert_raw ( ted:: Position :: before ( start_offset) , def. syntax ( ) ) ;
106
107
ted:: insert_raw ( ted:: Position :: before ( start_offset) , & make:: tokens:: blank_line ( ) ) ;
107
108
108
- update_variant ( & variant) ;
109
+ update_variant ( & variant, enum_ast . generic_param_list ( ) ) ;
109
110
} ,
110
111
)
111
112
}
@@ -149,7 +150,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
149
150
fn create_struct_def (
150
151
variant_name : ast:: Name ,
151
152
field_list : & Either < ast:: RecordFieldList , ast:: TupleFieldList > ,
152
- visibility : Option < ast:: Visibility > ,
153
+ enum_ : & ast:: Enum ,
153
154
) -> ast:: Struct {
154
155
let pub_vis = make:: visibility_pub ( ) ;
155
156
@@ -184,12 +185,30 @@ fn create_struct_def(
184
185
}
185
186
} ;
186
187
187
- make:: struct_ ( visibility, variant_name, None , field_list) . clone_for_update ( )
188
+ // FIXME: This uses all the generic params of the enum, but the variant might not use all of them.
189
+ make:: struct_ ( enum_. visibility ( ) , variant_name, enum_. generic_param_list ( ) , field_list)
190
+ . clone_for_update ( )
188
191
}
189
192
190
- fn update_variant ( variant : & ast:: Variant ) -> Option < ( ) > {
193
+ fn update_variant ( variant : & ast:: Variant , generic : Option < ast :: GenericParamList > ) -> Option < ( ) > {
191
194
let name = variant. name ( ) ?;
192
- let tuple_field = make:: tuple_field ( None , make:: ty ( & name. text ( ) ) ) ;
195
+ let ty = match generic {
196
+ // FIXME: This uses all the generic params of the enum, but the variant might not use all of them.
197
+ Some ( gpl) => {
198
+ let gpl = gpl. clone_for_update ( ) ;
199
+ gpl. generic_params ( ) . for_each ( |gp| {
200
+ match gp {
201
+ ast:: GenericParam :: LifetimeParam ( it) => it. type_bound_list ( ) ,
202
+ ast:: GenericParam :: TypeParam ( it) => it. type_bound_list ( ) ,
203
+ ast:: GenericParam :: ConstParam ( _) => return ,
204
+ }
205
+ . map ( |it| it. remove ( ) ) ;
206
+ } ) ;
207
+ make:: ty ( & format ! ( "{}<{}>" , name. text( ) , gpl. generic_params( ) . join( ", " ) ) )
208
+ }
209
+ None => make:: ty ( & name. text ( ) ) ,
210
+ } ;
211
+ let tuple_field = make:: tuple_field ( None , ty) ;
193
212
let replacement = make:: variant (
194
213
name,
195
214
Some ( ast:: FieldList :: TupleFieldList ( make:: tuple_field_list ( iter:: once ( tuple_field) ) ) ) ,
@@ -208,10 +227,9 @@ fn apply_references(
208
227
if let Some ( ( scope, path) ) = import {
209
228
insert_use ( & scope, mod_path_to_ast ( & path) , insert_use_cfg) ;
210
229
}
211
- ted:: insert_raw (
212
- ted:: Position :: before ( segment. syntax ( ) ) ,
213
- make:: path_from_text ( & format ! ( "{}" , segment) ) . clone_for_update ( ) . syntax ( ) ,
214
- ) ;
230
+ // deep clone to prevent cycle
231
+ let path = make:: path_from_segments ( iter:: once ( segment. clone_subtree ( ) ) , false ) ;
232
+ ted:: insert_raw ( ted:: Position :: before ( segment. syntax ( ) ) , path. clone_for_update ( ) . syntax ( ) ) ;
215
233
ted:: insert_raw ( ted:: Position :: before ( segment. syntax ( ) ) , make:: token ( T ! [ '(' ] ) ) ;
216
234
ted:: insert_raw ( ted:: Position :: after ( & node) , make:: token ( T ! [ ')' ] ) ) ;
217
235
}
@@ -278,6 +296,12 @@ mod tests {
278
296
279
297
use super :: * ;
280
298
299
+ fn check_not_applicable ( ra_fixture : & str ) {
300
+ let fixture =
301
+ format ! ( "//- /main.rs crate:main deps:core\n {}\n {}" , ra_fixture, FamousDefs :: FIXTURE ) ;
302
+ check_assist_not_applicable ( extract_struct_from_enum_variant, & fixture)
303
+ }
304
+
281
305
#[ test]
282
306
fn test_extract_struct_several_fields_tuple ( ) {
283
307
check_assist (
@@ -311,6 +335,17 @@ enum A { One(One) }"#,
311
335
) ;
312
336
}
313
337
338
+ #[ test]
339
+ fn test_extract_struct_keeps_generics ( ) {
340
+ check_assist (
341
+ extract_struct_from_enum_variant,
342
+ r"enum En<T> { Var { a: T$0 } }" ,
343
+ r#"struct Var<T>{ pub a: T }
344
+
345
+ enum En<T> { Var(Var<T>) }"# ,
346
+ ) ;
347
+ }
348
+
314
349
#[ test]
315
350
fn test_extract_struct_keep_comments_and_attrs_one_field_named ( ) {
316
351
check_assist (
@@ -610,12 +645,6 @@ fn foo() {
610
645
) ;
611
646
}
612
647
613
- fn check_not_applicable ( ra_fixture : & str ) {
614
- let fixture =
615
- format ! ( "//- /main.rs crate:main deps:core\n {}\n {}" , ra_fixture, FamousDefs :: FIXTURE ) ;
616
- check_assist_not_applicable ( extract_struct_from_enum_variant, & fixture)
617
- }
618
-
619
648
#[ test]
620
649
fn test_extract_enum_not_applicable_for_element_with_no_fields ( ) {
621
650
check_not_applicable ( "enum A { $0One }" ) ;
0 commit comments