@@ -71,35 +71,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
7171 syntax:: NodeOrToken :: Token ( t) => t. parent ( ) ?,
7272 } ;
7373 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 ( ) ?;
10175
102- let ( locals_used, has_await , self_param) = body. analyze ( & ctx. sema ) ;
76+ let ( locals_used, self_param) = body. analyze ( & ctx. sema ) ;
10377
10478 let anchor = if self_param. is_some ( ) { Anchor :: Method } else { Anchor :: Freestanding } ;
10579 let insert_after = node_to_insert_after ( & body, anchor) ?;
@@ -125,10 +99,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
12599
126100 let params = body. extracted_function_params ( ctx, locals_used. iter ( ) . copied ( ) ) ;
127101
128- let insert_comma = body
129- . parent ( )
130- . and_then ( ast:: MatchArm :: cast)
131- . map_or ( false , |it| it. comma_token ( ) . is_none ( ) ) ;
132102 let fun = Function {
133103 name : "fun_name" . to_string ( ) ,
134104 self_param,
@@ -137,18 +107,15 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
137107 ret_ty,
138108 body,
139109 outliving_locals,
110+ mods,
140111 } ;
141112
142113 let new_indent = IndentLevel :: from_node ( & insert_after) ;
143114 let old_indent = fun. body . indent_level ( ) ;
144115
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) ) ;
149117
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) ;
152119 let insert_offset = insert_after. text_range ( ) . end ( ) ;
153120 match ctx. config . snippet_cap {
154121 Some ( cap) => builder. insert_snippet ( cap, insert_offset, fn_def) ,
@@ -210,6 +177,7 @@ struct Function {
210177 ret_ty : RetType ,
211178 body : FunctionBody ,
212179 outliving_locals : Vec < OutlivedLocal > ,
180+ mods : Modifiers ,
213181}
214182
215183#[ derive( Debug ) ]
@@ -248,6 +216,13 @@ enum Anchor {
248216#[ derive( Debug ) ]
249217struct ControlFlow {
250218 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 ,
251226}
252227
253228/// Control flow that is exported from extracted function
@@ -605,13 +580,11 @@ impl FunctionBody {
605580 fn analyze (
606581 & self ,
607582 sema : & Semantics < RootDatabase > ,
608- ) -> ( FxIndexSet < Local > , bool , Option < ast:: SelfParam > ) {
583+ ) -> ( FxIndexSet < Local > , Option < ast:: SelfParam > ) {
609584 // FIXME: currently usages inside macros are not found
610- let mut has_await = false ;
611585 let mut self_param = None ;
612586 let mut res = FxIndexSet :: default ( ) ;
613587 self . walk_expr ( & mut |expr| {
614- has_await |= matches ! ( expr, ast:: Expr :: AwaitExpr ( _) ) ;
615588 let name_ref = match expr {
616589 ast:: Expr :: PathExpr ( path_expr) => {
617590 path_expr. path ( ) . and_then ( |it| it. as_single_name_ref ( ) )
@@ -644,7 +617,60 @@ impl FunctionBody {
644617 }
645618 }
646619 } ) ;
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 } )
648674 }
649675
650676 fn return_ty ( & self , ctx : & AssistContext ) -> Option < RetType > {
@@ -673,6 +699,7 @@ impl FunctionBody {
673699 let mut try_expr = None ;
674700 let mut break_expr = None ;
675701 let mut continue_expr = None ;
702+ let mut is_async = false ;
676703
677704 let mut loop_depth = 0 ;
678705
@@ -703,6 +730,7 @@ impl FunctionBody {
703730 ast:: Expr :: ContinueExpr ( it) if loop_depth == 0 => {
704731 continue_expr = Some ( it) ;
705732 }
733+ ast:: Expr :: AwaitExpr ( _) => is_async = true ,
706734 _ => { }
707735 }
708736 false
@@ -746,7 +774,7 @@ impl FunctionBody {
746774 ( None , None , None , None ) => None ,
747775 } ;
748776
749- Some ( ControlFlow { kind } )
777+ Some ( ControlFlow { kind, is_async } )
750778 }
751779 /// find variables that should be extracted as params
752780 ///
@@ -1031,12 +1059,7 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
10311059 last_ancestor
10321060}
10331061
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 {
10401063 let ret_ty = fun. return_type ( ctx) ;
10411064
10421065 let args = fun. params . iter ( ) . map ( |param| param. to_arg ( ctx) ) ;
@@ -1053,6 +1076,8 @@ fn make_call(
10531076
10541077 let expr = handler. make_call_expr ( call_expr) . indent ( indent) ;
10551078
1079+ let mut_modifier = |var : & OutlivedLocal | if var. mut_usage_outside_body { "mut " } else { "" } ;
1080+
10561081 let mut buf = String :: new ( ) ;
10571082 match fun. outliving_locals . as_slice ( ) {
10581083 [ ] => { }
@@ -1068,18 +1093,18 @@ fn make_call(
10681093 buf. push_str ( ") = " ) ;
10691094 }
10701095 }
1071- fn mut_modifier ( var : & OutlivedLocal ) -> & ' static str {
1072- if var. mut_usage_outside_body {
1073- "mut "
1074- } else {
1075- ""
1076- }
1077- }
10781096 format_to ! ( buf, "{}" , expr) ;
1079- if body_contains_await {
1097+ if fun . control_flow . is_async {
10801098 buf. push_str ( ".await" ) ;
10811099 }
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 ( ) ) {
10831108 buf. push ( ';' ) ;
10841109 }
10851110 buf
@@ -1209,17 +1234,32 @@ fn format_function(
12091234 fun : & Function ,
12101235 old_indent : IndentLevel ,
12111236 new_indent : IndentLevel ,
1212- body_contains_await : bool ,
1213- in_tail : bool ,
12141237) -> String {
12151238 let mut fn_def = String :: new ( ) ;
12161239 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 { "" } ;
12201244 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+ ) ,
12231263 }
12241264 if let Some ( ret_ty) = ret_ty {
12251265 format_to ! ( fn_def, " {}" , ret_ty) ;
@@ -1236,15 +1276,13 @@ impl Function {
12361276 make:: param_list ( self_param, params)
12371277 }
12381278
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 > {
12451280 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+ } ;
12481286 let ret_ty = match & handler {
12491287 FlowHandler :: None => {
12501288 if matches ! ( fun_ty, FunType :: Unit ) {
@@ -1312,10 +1350,13 @@ fn make_body(
13121350 old_indent : IndentLevel ,
13131351 new_indent : IndentLevel ,
13141352 fun : & Function ,
1315- in_tail : bool ,
13161353) -> ast:: BlockExpr {
13171354 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+ } ;
13191360 let block = match & fun. body {
13201361 FunctionBody :: Expr ( expr) => {
13211362 let expr = rewrite_body_segment ( ctx, & fun. params , & handler, expr. syntax ( ) ) ;
@@ -3929,6 +3970,44 @@ fn $0fun_name() -> Result<(), i64> {
39293970 Result::<i32, i64>::Ok(0)?;
39303971 Ok(())
39313972}
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+ }
39324011"# ,
39334012 ) ;
39344013 }
0 commit comments