@@ -18,7 +18,7 @@ use syntax::{
18
18
ast:: {
19
19
self ,
20
20
edit:: { AstNodeEdit , IndentLevel } ,
21
- AstNode ,
21
+ AstNode , HasGenericParams ,
22
22
} ,
23
23
match_ast, ted, SyntaxElement ,
24
24
SyntaxKind :: { self , COMMENT } ,
@@ -266,6 +266,8 @@ struct ContainerInfo {
266
266
parent_loop : Option < SyntaxNode > ,
267
267
/// The function's return type, const's type etc.
268
268
ret_type : Option < hir:: Type > ,
269
+ generic_param_list : Option < ast:: GenericParamList > ,
270
+ where_clause : Option < ast:: WhereClause > ,
269
271
}
270
272
271
273
/// Control flow that is exported from extracted function
@@ -676,11 +678,11 @@ impl FunctionBody {
676
678
parent_loop. get_or_insert ( loop_. syntax ( ) . clone ( ) ) ;
677
679
}
678
680
} ;
679
- let ( is_const, expr, ty) = loop {
681
+ let ( is_const, expr, ty, generic_param_list , where_clause ) = loop {
680
682
let anc = ancestors. next ( ) ?;
681
683
break match_ast ! {
682
684
match anc {
683
- ast:: ClosureExpr ( closure) => ( false , closure. body( ) , infer_expr_opt( closure. body( ) ) ) ,
685
+ ast:: ClosureExpr ( closure) => ( false , closure. body( ) , infer_expr_opt( closure. body( ) ) , closure . generic_param_list ( ) , None ) ,
684
686
ast:: BlockExpr ( block_expr) => {
685
687
let ( constness, block) = match block_expr. modifier( ) {
686
688
Some ( ast:: BlockModifier :: Const ( _) ) => ( true , block_expr) ,
@@ -689,7 +691,7 @@ impl FunctionBody {
689
691
_ => continue ,
690
692
} ;
691
693
let expr = Some ( ast:: Expr :: BlockExpr ( block) ) ;
692
- ( constness, expr. clone( ) , infer_expr_opt( expr) )
694
+ ( constness, expr. clone( ) , infer_expr_opt( expr) , None , None )
693
695
} ,
694
696
ast:: Fn ( fn_) => {
695
697
let func = sema. to_def( & fn_) ?;
@@ -699,23 +701,23 @@ impl FunctionBody {
699
701
ret_ty = async_ret;
700
702
}
701
703
}
702
- ( fn_. const_token( ) . is_some( ) , fn_. body( ) . map( ast:: Expr :: BlockExpr ) , Some ( ret_ty) )
704
+ ( fn_. const_token( ) . is_some( ) , fn_. body( ) . map( ast:: Expr :: BlockExpr ) , Some ( ret_ty) , fn_ . generic_param_list ( ) , fn_ . where_clause ( ) )
703
705
} ,
704
706
ast:: Static ( statik) => {
705
- ( true , statik. body( ) , Some ( sema. to_def( & statik) ?. ty( sema. db) ) )
707
+ ( true , statik. body( ) , Some ( sema. to_def( & statik) ?. ty( sema. db) ) , None , None )
706
708
} ,
707
709
ast:: ConstArg ( ca) => {
708
- ( true , ca. expr( ) , infer_expr_opt( ca. expr( ) ) )
710
+ ( true , ca. expr( ) , infer_expr_opt( ca. expr( ) ) , None , None )
709
711
} ,
710
712
ast:: Const ( konst) => {
711
- ( true , konst. body( ) , Some ( sema. to_def( & konst) ?. ty( sema. db) ) )
713
+ ( true , konst. body( ) , Some ( sema. to_def( & konst) ?. ty( sema. db) ) , None , None )
712
714
} ,
713
715
ast:: ConstParam ( cp) => {
714
- ( true , cp. default_val( ) , Some ( sema. to_def( & cp) ?. ty( sema. db) ) )
716
+ ( true , cp. default_val( ) , Some ( sema. to_def( & cp) ?. ty( sema. db) ) , None , None )
715
717
} ,
716
718
ast:: ConstBlockPat ( cbp) => {
717
719
let expr = cbp. block_expr( ) . map( ast:: Expr :: BlockExpr ) ;
718
- ( true , expr. clone( ) , infer_expr_opt( expr) )
720
+ ( true , expr. clone( ) , infer_expr_opt( expr) , None , None )
719
721
} ,
720
722
ast:: Variant ( __) => return None ,
721
723
ast:: Meta ( __) => return None ,
@@ -743,7 +745,14 @@ impl FunctionBody {
743
745
container_tail. zip ( self . tail_expr ( ) ) . map_or ( false , |( container_tail, body_tail) | {
744
746
container_tail. syntax ( ) . text_range ( ) . contains_range ( body_tail. syntax ( ) . text_range ( ) )
745
747
} ) ;
746
- Some ( ContainerInfo { is_in_tail, is_const, parent_loop, ret_type : ty } )
748
+ Some ( ContainerInfo {
749
+ is_in_tail,
750
+ is_const,
751
+ parent_loop,
752
+ ret_type : ty,
753
+ generic_param_list,
754
+ where_clause,
755
+ } )
747
756
}
748
757
749
758
fn return_ty ( & self , ctx : & AssistContext ) -> Option < RetType > {
@@ -890,6 +899,7 @@ impl FunctionBody {
890
899
// if the var is not used but defined outside a loop we are extracting from we can't move it either
891
900
// as the function will reuse it in the next iteration.
892
901
let move_local = ( !has_usages && defined_outside_parent_loop) || ty. is_reference ( ) ;
902
+
893
903
Param { var, ty, move_local, requires_mut, is_copy }
894
904
} )
895
905
. collect ( )
@@ -1311,26 +1321,32 @@ fn format_function(
1311
1321
let const_kw = if fun. mods . is_const { "const " } else { "" } ;
1312
1322
let async_kw = if fun. control_flow . is_async { "async " } else { "" } ;
1313
1323
let unsafe_kw = if fun. control_flow . is_unsafe { "unsafe " } else { "" } ;
1324
+ let generic_params = format_generic_param_list ( fun) ;
1325
+ let where_clause = format_where_clause ( fun) ;
1314
1326
match ctx. config . snippet_cap {
1315
1327
Some ( _) => format_to ! (
1316
1328
fn_def,
1317
- "\n \n {}{}{}{}fn $0{}{}" ,
1329
+ "\n \n {}{}{}{}fn $0{}{}{}{} " ,
1318
1330
new_indent,
1319
1331
const_kw,
1320
1332
async_kw,
1321
1333
unsafe_kw,
1322
1334
fun. name,
1323
- params
1335
+ generic_params,
1336
+ params,
1337
+ where_clause
1324
1338
) ,
1325
1339
None => format_to ! (
1326
1340
fn_def,
1327
- "\n \n {}{}{}{}fn {}{}" ,
1341
+ "\n \n {}{}{}{}fn {}{}{}{} " ,
1328
1342
new_indent,
1329
1343
const_kw,
1330
1344
async_kw,
1331
1345
unsafe_kw,
1332
1346
fun. name,
1333
- params
1347
+ generic_params,
1348
+ params,
1349
+ where_clause,
1334
1350
) ,
1335
1351
}
1336
1352
if let Some ( ret_ty) = ret_ty {
@@ -1341,6 +1357,26 @@ fn format_function(
1341
1357
fn_def
1342
1358
}
1343
1359
1360
+ fn format_generic_param_list ( fun : & Function ) -> String {
1361
+ format_or_empty_string ( & fun. mods . generic_param_list , "" . to_string ( ) )
1362
+ }
1363
+
1364
+ fn format_where_clause ( fun : & Function ) -> String {
1365
+ format_or_empty_string ( & fun. mods . where_clause , " " . to_string ( ) )
1366
+ }
1367
+
1368
+ fn format_or_empty_string < T : std:: fmt:: Display > (
1369
+ it : & Option < T > ,
1370
+ mut starting_with : String ,
1371
+ ) -> String {
1372
+ it. as_ref ( )
1373
+ . and_then ( |it| {
1374
+ format_to ! ( starting_with, "{}" , it) ;
1375
+ Some ( starting_with)
1376
+ } )
1377
+ . unwrap_or_default ( )
1378
+ }
1379
+
1344
1380
impl Function {
1345
1381
fn make_param_list ( & self , ctx : & AssistContext , module : hir:: Module ) -> ast:: ParamList {
1346
1382
let self_param = self . self_param . clone ( ) ;
@@ -4709,6 +4745,48 @@ fn $0fun_name() {
4709
4745
/* a comment */
4710
4746
let x = 0;
4711
4747
}
4748
+ "# ,
4749
+ ) ;
4750
+ }
4751
+
4752
+ #[ test]
4753
+ fn preserve_generics ( ) {
4754
+ check_assist (
4755
+ extract_function,
4756
+ r#"
4757
+ fn func<T: Debug>(i: T) {
4758
+ $0foo(i);$0
4759
+ }
4760
+ "# ,
4761
+ r#"
4762
+ fn func<T: Debug>(i: T) {
4763
+ fun_name(i);
4764
+ }
4765
+
4766
+ fn $0fun_name<T: Debug>(i: T) {
4767
+ foo(i);
4768
+ }
4769
+ "# ,
4770
+ ) ;
4771
+ }
4772
+
4773
+ #[ test]
4774
+ fn preserve_where_clause ( ) {
4775
+ check_assist (
4776
+ extract_function,
4777
+ r#"
4778
+ fn func<T>(i: T) where T: Debug {
4779
+ $0foo(i);$0
4780
+ }
4781
+ "# ,
4782
+ r#"
4783
+ fn func<T>(i: T) where T: Debug {
4784
+ fun_name(i);
4785
+ }
4786
+
4787
+ fn $0fun_name<T>(i: T) where T: Debug {
4788
+ foo(i);
4789
+ }
4712
4790
"# ,
4713
4791
) ;
4714
4792
}
0 commit comments