@@ -71,35 +71,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
71
71
syntax:: NodeOrToken :: Token ( t) => t. parent ( ) ?,
72
72
} ;
73
73
let body = extraction_target ( & node, range) ?;
74
- let container_expr = body. parent ( ) ?. ancestors ( ) . find_map ( |it| {
75
- // double Option as we want to short circuit
76
- let res = match_ast ! {
77
- match it {
78
- ast:: ClosureExpr ( closure) => closure. body( ) ,
79
- ast:: EffectExpr ( effect) => effect. block_expr( ) . map( ast:: Expr :: BlockExpr ) ,
80
- ast:: Fn ( fn_) => fn_. body( ) . map( ast:: Expr :: BlockExpr ) ,
81
- ast:: Static ( statik) => statik. body( ) ,
82
- ast:: ConstArg ( ca) => ca. expr( ) ,
83
- ast:: Const ( konst) => konst. body( ) ,
84
- ast:: ConstParam ( cp) => cp. default_val( ) ,
85
- ast:: ConstBlockPat ( cbp) => cbp. block_expr( ) . map( ast:: Expr :: BlockExpr ) ,
86
- ast:: Variant ( __) => None ,
87
- ast:: Meta ( __) => None ,
88
- _ => return None ,
89
- }
90
- } ;
91
- Some ( res)
92
- } ) ??;
93
- let container_tail = match container_expr {
94
- ast:: Expr :: BlockExpr ( block) => block. tail_expr ( ) ,
95
- expr => Some ( expr) ,
96
- } ;
97
- let in_tail =
98
- container_tail. zip ( body. tail_expr ( ) ) . map_or ( false , |( container_tail, body_tail) | {
99
- container_tail. syntax ( ) . text_range ( ) . contains_range ( body_tail. syntax ( ) . text_range ( ) )
100
- } ) ;
74
+ let mods = body. analyze_container ( ) ?;
101
75
102
- let ( locals_used, has_await , self_param) = body. analyze ( & ctx. sema ) ;
76
+ let ( locals_used, self_param) = body. analyze ( & ctx. sema ) ;
103
77
104
78
let anchor = if self_param. is_some ( ) { Anchor :: Method } else { Anchor :: Freestanding } ;
105
79
let insert_after = node_to_insert_after ( & body, anchor) ?;
@@ -125,10 +99,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
125
99
126
100
let params = body. extracted_function_params ( ctx, locals_used. iter ( ) . copied ( ) ) ;
127
101
128
- let insert_comma = body
129
- . parent ( )
130
- . and_then ( ast:: MatchArm :: cast)
131
- . map_or ( false , |it| it. comma_token ( ) . is_none ( ) ) ;
132
102
let fun = Function {
133
103
name : "fun_name" . to_string ( ) ,
134
104
self_param,
@@ -137,18 +107,15 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
137
107
ret_ty,
138
108
body,
139
109
outliving_locals,
110
+ mods,
140
111
} ;
141
112
142
113
let new_indent = IndentLevel :: from_node ( & insert_after) ;
143
114
let old_indent = fun. body . indent_level ( ) ;
144
115
145
- builder. replace ( target_range, make_call ( ctx, & fun, old_indent, has_await) ) ;
146
- if insert_comma {
147
- builder. insert ( target_range. end ( ) , "," ) ;
148
- }
116
+ builder. replace ( target_range, make_call ( ctx, & fun, old_indent) ) ;
149
117
150
- let fn_def =
151
- format_function ( ctx, module, & fun, old_indent, new_indent, has_await, in_tail) ;
118
+ let fn_def = format_function ( ctx, module, & fun, old_indent, new_indent) ;
152
119
let insert_offset = insert_after. text_range ( ) . end ( ) ;
153
120
match ctx. config . snippet_cap {
154
121
Some ( cap) => builder. insert_snippet ( cap, insert_offset, fn_def) ,
@@ -210,6 +177,7 @@ struct Function {
210
177
ret_ty : RetType ,
211
178
body : FunctionBody ,
212
179
outliving_locals : Vec < OutlivedLocal > ,
180
+ mods : Modifiers ,
213
181
}
214
182
215
183
#[ derive( Debug ) ]
@@ -248,6 +216,13 @@ enum Anchor {
248
216
#[ derive( Debug ) ]
249
217
struct ControlFlow {
250
218
kind : Option < FlowKind > ,
219
+ is_async : bool ,
220
+ }
221
+
222
+ #[ derive( Copy , Clone , Debug ) ]
223
+ struct Modifiers {
224
+ is_const : bool ,
225
+ is_in_tail : bool ,
251
226
}
252
227
253
228
/// Control flow that is exported from extracted function
@@ -605,13 +580,11 @@ impl FunctionBody {
605
580
fn analyze (
606
581
& self ,
607
582
sema : & Semantics < RootDatabase > ,
608
- ) -> ( FxIndexSet < Local > , bool , Option < ast:: SelfParam > ) {
583
+ ) -> ( FxIndexSet < Local > , Option < ast:: SelfParam > ) {
609
584
// FIXME: currently usages inside macros are not found
610
- let mut has_await = false ;
611
585
let mut self_param = None ;
612
586
let mut res = FxIndexSet :: default ( ) ;
613
587
self . walk_expr ( & mut |expr| {
614
- has_await |= matches ! ( expr, ast:: Expr :: AwaitExpr ( _) ) ;
615
588
let name_ref = match expr {
616
589
ast:: Expr :: PathExpr ( path_expr) => {
617
590
path_expr. path ( ) . and_then ( |it| it. as_single_name_ref ( ) )
@@ -644,7 +617,60 @@ impl FunctionBody {
644
617
}
645
618
}
646
619
} ) ;
647
- ( res, has_await, self_param)
620
+ ( res, self_param)
621
+ }
622
+
623
+ fn analyze_container ( & self ) -> Option < Modifiers > {
624
+ let mut is_const = false ;
625
+ let container_expr = self . parent ( ) ?. ancestors ( ) . find_map ( |it| {
626
+ // double Option as we want to short circuit
627
+ let res = match_ast ! {
628
+ match it {
629
+ ast:: ClosureExpr ( closure) => closure. body( ) ,
630
+ ast:: EffectExpr ( effect) => {
631
+ is_const = effect. const_token( ) . is_some( ) ;
632
+ effect. block_expr( ) . map( ast:: Expr :: BlockExpr )
633
+ } ,
634
+ ast:: Fn ( fn_) => {
635
+ is_const = fn_. const_token( ) . is_some( ) ;
636
+ fn_. body( ) . map( ast:: Expr :: BlockExpr )
637
+ } ,
638
+ ast:: Static ( statik) => {
639
+ is_const = true ;
640
+ statik. body( )
641
+ } ,
642
+ ast:: ConstArg ( ca) => {
643
+ is_const = true ;
644
+ ca. expr( )
645
+ } ,
646
+ ast:: Const ( konst) => {
647
+ is_const = true ;
648
+ konst. body( )
649
+ } ,
650
+ ast:: ConstParam ( cp) => {
651
+ is_const = true ;
652
+ cp. default_val( )
653
+ } ,
654
+ ast:: ConstBlockPat ( cbp) => {
655
+ is_const = true ;
656
+ cbp. block_expr( ) . map( ast:: Expr :: BlockExpr )
657
+ } ,
658
+ ast:: Variant ( __) => None ,
659
+ ast:: Meta ( __) => None ,
660
+ _ => return None ,
661
+ }
662
+ } ;
663
+ Some ( res)
664
+ } ) ??;
665
+ let container_tail = match container_expr {
666
+ ast:: Expr :: BlockExpr ( block) => block. tail_expr ( ) ,
667
+ expr => Some ( expr) ,
668
+ } ;
669
+ let is_in_tail =
670
+ container_tail. zip ( self . tail_expr ( ) ) . map_or ( false , |( container_tail, body_tail) | {
671
+ container_tail. syntax ( ) . text_range ( ) . contains_range ( body_tail. syntax ( ) . text_range ( ) )
672
+ } ) ;
673
+ Some ( Modifiers { is_in_tail, is_const } )
648
674
}
649
675
650
676
fn return_ty ( & self , ctx : & AssistContext ) -> Option < RetType > {
@@ -673,6 +699,7 @@ impl FunctionBody {
673
699
let mut try_expr = None ;
674
700
let mut break_expr = None ;
675
701
let mut continue_expr = None ;
702
+ let mut is_async = false ;
676
703
677
704
let mut loop_depth = 0 ;
678
705
@@ -703,6 +730,7 @@ impl FunctionBody {
703
730
ast:: Expr :: ContinueExpr ( it) if loop_depth == 0 => {
704
731
continue_expr = Some ( it) ;
705
732
}
733
+ ast:: Expr :: AwaitExpr ( _) => is_async = true ,
706
734
_ => { }
707
735
}
708
736
false
@@ -746,7 +774,7 @@ impl FunctionBody {
746
774
( None , None , None , None ) => None ,
747
775
} ;
748
776
749
- Some ( ControlFlow { kind } )
777
+ Some ( ControlFlow { kind, is_async } )
750
778
}
751
779
/// find variables that should be extracted as params
752
780
///
@@ -1031,12 +1059,7 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
1031
1059
last_ancestor
1032
1060
}
1033
1061
1034
- fn make_call (
1035
- ctx : & AssistContext ,
1036
- fun : & Function ,
1037
- indent : IndentLevel ,
1038
- body_contains_await : bool ,
1039
- ) -> String {
1062
+ fn make_call ( ctx : & AssistContext , fun : & Function , indent : IndentLevel ) -> String {
1040
1063
let ret_ty = fun. return_type ( ctx) ;
1041
1064
1042
1065
let args = fun. params . iter ( ) . map ( |param| param. to_arg ( ctx) ) ;
@@ -1053,6 +1076,8 @@ fn make_call(
1053
1076
1054
1077
let expr = handler. make_call_expr ( call_expr) . indent ( indent) ;
1055
1078
1079
+ let mut_modifier = |var : & OutlivedLocal | if var. mut_usage_outside_body { "mut " } else { "" } ;
1080
+
1056
1081
let mut buf = String :: new ( ) ;
1057
1082
match fun. outliving_locals . as_slice ( ) {
1058
1083
[ ] => { }
@@ -1068,18 +1093,18 @@ fn make_call(
1068
1093
buf. push_str ( ") = " ) ;
1069
1094
}
1070
1095
}
1071
- fn mut_modifier ( var : & OutlivedLocal ) -> & ' static str {
1072
- if var. mut_usage_outside_body {
1073
- "mut "
1074
- } else {
1075
- ""
1076
- }
1077
- }
1078
1096
format_to ! ( buf, "{}" , expr) ;
1079
- if body_contains_await {
1097
+ if fun . control_flow . is_async {
1080
1098
buf. push_str ( ".await" ) ;
1081
1099
}
1082
- if fun. ret_ty . is_unit ( ) && ( !fun. outliving_locals . is_empty ( ) || !expr. is_block_like ( ) ) {
1100
+ let insert_comma = fun
1101
+ . body
1102
+ . parent ( )
1103
+ . and_then ( ast:: MatchArm :: cast)
1104
+ . map_or ( false , |it| it. comma_token ( ) . is_none ( ) ) ;
1105
+ if insert_comma {
1106
+ buf. push ( ',' ) ;
1107
+ } else if fun. ret_ty . is_unit ( ) && ( !fun. outliving_locals . is_empty ( ) || !expr. is_block_like ( ) ) {
1083
1108
buf. push ( ';' ) ;
1084
1109
}
1085
1110
buf
@@ -1209,17 +1234,32 @@ fn format_function(
1209
1234
fun : & Function ,
1210
1235
old_indent : IndentLevel ,
1211
1236
new_indent : IndentLevel ,
1212
- body_contains_await : bool ,
1213
- in_tail : bool ,
1214
1237
) -> String {
1215
1238
let mut fn_def = String :: new ( ) ;
1216
1239
let params = fun. make_param_list ( ctx, module) ;
1217
- let ret_ty = fun. make_ret_ty ( ctx, module, in_tail) ;
1218
- let body = make_body ( ctx, old_indent, new_indent, fun, in_tail) ;
1219
- let async_kw = if body_contains_await { "async " } else { "" } ;
1240
+ let ret_ty = fun. make_ret_ty ( ctx, module) ;
1241
+ let body = make_body ( ctx, old_indent, new_indent, fun) ;
1242
+ let const_kw = if fun. mods . is_const { "const " } else { "" } ;
1243
+ let async_kw = if fun. control_flow . is_async { "async " } else { "" } ;
1220
1244
match ctx. config . snippet_cap {
1221
- Some ( _) => format_to ! ( fn_def, "\n \n {}{}fn $0{}{}" , new_indent, async_kw, fun. name, params) ,
1222
- None => format_to ! ( fn_def, "\n \n {}{}fn {}{}" , new_indent, async_kw, fun. name, params) ,
1245
+ Some ( _) => format_to ! (
1246
+ fn_def,
1247
+ "\n \n {}{}{}fn $0{}{}" ,
1248
+ new_indent,
1249
+ const_kw,
1250
+ async_kw,
1251
+ fun. name,
1252
+ params
1253
+ ) ,
1254
+ None => format_to ! (
1255
+ fn_def,
1256
+ "\n \n {}{}{}fn {}{}" ,
1257
+ new_indent,
1258
+ const_kw,
1259
+ async_kw,
1260
+ fun. name,
1261
+ params
1262
+ ) ,
1223
1263
}
1224
1264
if let Some ( ret_ty) = ret_ty {
1225
1265
format_to ! ( fn_def, " {}" , ret_ty) ;
@@ -1236,15 +1276,13 @@ impl Function {
1236
1276
make:: param_list ( self_param, params)
1237
1277
}
1238
1278
1239
- fn make_ret_ty (
1240
- & self ,
1241
- ctx : & AssistContext ,
1242
- module : hir:: Module ,
1243
- in_tail : bool ,
1244
- ) -> Option < ast:: RetType > {
1279
+ fn make_ret_ty ( & self , ctx : & AssistContext , module : hir:: Module ) -> Option < ast:: RetType > {
1245
1280
let fun_ty = self . return_type ( ctx) ;
1246
- let handler =
1247
- if in_tail { FlowHandler :: None } else { FlowHandler :: from_ret_ty ( self , & fun_ty) } ;
1281
+ let handler = if self . mods . is_in_tail {
1282
+ FlowHandler :: None
1283
+ } else {
1284
+ FlowHandler :: from_ret_ty ( self , & fun_ty)
1285
+ } ;
1248
1286
let ret_ty = match & handler {
1249
1287
FlowHandler :: None => {
1250
1288
if matches ! ( fun_ty, FunType :: Unit ) {
@@ -1312,10 +1350,13 @@ fn make_body(
1312
1350
old_indent : IndentLevel ,
1313
1351
new_indent : IndentLevel ,
1314
1352
fun : & Function ,
1315
- in_tail : bool ,
1316
1353
) -> ast:: BlockExpr {
1317
1354
let ret_ty = fun. return_type ( ctx) ;
1318
- let handler = if in_tail { FlowHandler :: None } else { FlowHandler :: from_ret_ty ( fun, & ret_ty) } ;
1355
+ let handler = if fun. mods . is_in_tail {
1356
+ FlowHandler :: None
1357
+ } else {
1358
+ FlowHandler :: from_ret_ty ( fun, & ret_ty)
1359
+ } ;
1319
1360
let block = match & fun. body {
1320
1361
FunctionBody :: Expr ( expr) => {
1321
1362
let expr = rewrite_body_segment ( ctx, & fun. params , & handler, expr. syntax ( ) ) ;
@@ -3929,6 +3970,44 @@ fn $0fun_name() -> Result<(), i64> {
3929
3970
Result::<i32, i64>::Ok(0)?;
3930
3971
Ok(())
3931
3972
}
3973
+ "# ,
3974
+ ) ;
3975
+ }
3976
+
3977
+ #[ test]
3978
+ fn extract_knows_const ( ) {
3979
+ check_assist (
3980
+ extract_function,
3981
+ r#"
3982
+ const fn foo() {
3983
+ $0()$0
3984
+ }
3985
+ "# ,
3986
+ r#"
3987
+ const fn foo() {
3988
+ fun_name();
3989
+ }
3990
+
3991
+ const fn $0fun_name() {
3992
+ ()
3993
+ }
3994
+ "# ,
3995
+ ) ;
3996
+ check_assist (
3997
+ extract_function,
3998
+ r#"
3999
+ const FOO: () = {
4000
+ $0()$0
4001
+ };
4002
+ "# ,
4003
+ r#"
4004
+ const FOO: () = {
4005
+ fun_name();
4006
+ };
4007
+
4008
+ const fn $0fun_name() {
4009
+ ()
4010
+ }
3932
4011
"# ,
3933
4012
) ;
3934
4013
}
0 commit comments