@@ -75,7 +75,8 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
7575 let insert_after = scope_for_fn_insertion ( & body, anchor) ?;
7676 let module = ctx. sema . scope ( & insert_after) . module ( ) ?;
7777
78- let vars_defined_in_body_and_outlive = vars_defined_in_body_and_outlive ( ctx, & body) ;
78+ let vars_defined_in_body_and_outlive =
79+ vars_defined_in_body_and_outlive ( ctx, & body, & node. parent ( ) . as_ref ( ) . unwrap_or ( & node) ) ;
7980 let ret_ty = body_return_ty ( ctx, & body) ?;
8081
8182 // FIXME: we compute variables that outlive here just to check `never!` condition
@@ -257,7 +258,7 @@ struct Function {
257258 control_flow : ControlFlow ,
258259 ret_ty : RetType ,
259260 body : FunctionBody ,
260- vars_defined_in_body_and_outlive : Vec < Local > ,
261+ vars_defined_in_body_and_outlive : Vec < OutlivedLocal > ,
261262}
262263
263264#[ derive( Debug ) ]
@@ -296,9 +297,9 @@ impl Function {
296297 RetType :: Expr ( ty) => FunType :: Single ( ty. clone ( ) ) ,
297298 RetType :: Stmt => match self . vars_defined_in_body_and_outlive . as_slice ( ) {
298299 [ ] => FunType :: Unit ,
299- [ var] => FunType :: Single ( var. ty ( ctx. db ( ) ) ) ,
300+ [ var] => FunType :: Single ( var. local . ty ( ctx. db ( ) ) ) ,
300301 vars => {
301- let types = vars. iter ( ) . map ( |v| v. ty ( ctx. db ( ) ) ) . collect ( ) ;
302+ let types = vars. iter ( ) . map ( |v| v. local . ty ( ctx. db ( ) ) ) . collect ( ) ;
302303 FunType :: Tuple ( types)
303304 }
304305 } ,
@@ -562,6 +563,12 @@ impl HasTokenAtOffset for FunctionBody {
562563 }
563564}
564565
566+ #[ derive( Debug ) ]
567+ struct OutlivedLocal {
568+ local : Local ,
569+ mut_usage_outside_body : bool ,
570+ }
571+
565572/// Try to guess what user wants to extract
566573///
567574/// We have basically have two cases:
@@ -707,10 +714,10 @@ fn has_exclusive_usages(ctx: &AssistContext, usages: &LocalUsages, body: &Functi
707714 . any ( |reference| reference_is_exclusive ( reference, body, ctx) )
708715}
709716
710- /// checks if this reference requires `&mut` access inside body
717+ /// checks if this reference requires `&mut` access inside node
711718fn reference_is_exclusive (
712719 reference : & FileReference ,
713- body : & FunctionBody ,
720+ node : & dyn HasTokenAtOffset ,
714721 ctx : & AssistContext ,
715722) -> bool {
716723 // we directly modify variable with set: `n = 0`, `n += 1`
@@ -719,7 +726,7 @@ fn reference_is_exclusive(
719726 }
720727
721728 // we take `&mut` reference to variable: `&mut v`
722- let path = match path_element_of_reference ( body , reference) {
729+ let path = match path_element_of_reference ( node , reference) {
723730 Some ( path) => path,
724731 None => return false ,
725732 } ;
@@ -820,10 +827,16 @@ fn vars_defined_in_body(body: &FunctionBody, ctx: &AssistContext) -> Vec<Local>
820827}
821828
822829/// list local variables defined inside `body` that should be returned from extracted function
823- fn vars_defined_in_body_and_outlive ( ctx : & AssistContext , body : & FunctionBody ) -> Vec < Local > {
824- let mut vars_defined_in_body = vars_defined_in_body ( & body, ctx) ;
825- vars_defined_in_body. retain ( |var| var_outlives_body ( ctx, body, var) ) ;
830+ fn vars_defined_in_body_and_outlive (
831+ ctx : & AssistContext ,
832+ body : & FunctionBody ,
833+ parent : & SyntaxNode ,
834+ ) -> Vec < OutlivedLocal > {
835+ let vars_defined_in_body = vars_defined_in_body ( & body, ctx) ;
826836 vars_defined_in_body
837+ . into_iter ( )
838+ . filter_map ( |var| var_outlives_body ( ctx, body, var, parent) )
839+ . collect ( )
827840}
828841
829842/// checks if the relevant local was defined before(outside of) body
@@ -843,11 +856,23 @@ fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
843856 }
844857}
845858
846- /// checks if local variable is used after(outside of) body
847- fn var_outlives_body ( ctx : & AssistContext , body : & FunctionBody , var : & Local ) -> bool {
848- let usages = LocalUsages :: find ( ctx, * var) ;
859+ /// returns usage details if local variable is used after(outside of) body
860+ fn var_outlives_body (
861+ ctx : & AssistContext ,
862+ body : & FunctionBody ,
863+ var : Local ,
864+ parent : & SyntaxNode ,
865+ ) -> Option < OutlivedLocal > {
866+ let usages = LocalUsages :: find ( ctx, var) ;
849867 let has_usages = usages. iter ( ) . any ( |reference| body. preceedes_range ( reference. range ) ) ;
850- has_usages
868+ if !has_usages {
869+ return None ;
870+ }
871+ let has_mut_usages = usages
872+ . iter ( )
873+ . filter ( |reference| body. preceedes_range ( reference. range ) )
874+ . any ( |reference| reference_is_exclusive ( reference, parent, ctx) ) ;
875+ Some ( OutlivedLocal { local : var, mut_usage_outside_body : has_mut_usages } )
851876}
852877
853878fn body_return_ty ( ctx : & AssistContext , body : & FunctionBody ) -> Option < RetType > {
@@ -927,16 +952,25 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
927952 let mut buf = String :: new ( ) ;
928953 match fun. vars_defined_in_body_and_outlive . as_slice ( ) {
929954 [ ] => { }
930- [ var] => format_to ! ( buf, "let {} = " , var. name( ctx. db( ) ) . unwrap( ) ) ,
955+ [ var] => {
956+ format_to ! ( buf, "let {}{} = " , mut_modifier( var) , var. local. name( ctx. db( ) ) . unwrap( ) )
957+ }
931958 [ v0, vs @ ..] => {
932959 buf. push_str ( "let (" ) ;
933- format_to ! ( buf, "{}" , v0 . name( ctx. db( ) ) . unwrap( ) ) ;
960+ format_to ! ( buf, "{}{} " , mut_modifier ( v0 ) , v0 . local . name( ctx. db( ) ) . unwrap( ) ) ;
934961 for var in vs {
935- format_to ! ( buf, ", {}" , var. name( ctx. db( ) ) . unwrap( ) ) ;
962+ format_to ! ( buf, ", {}{} " , mut_modifier ( var) , var . local . name( ctx. db( ) ) . unwrap( ) ) ;
936963 }
937964 buf. push_str ( ") = " ) ;
938965 }
939966 }
967+ fn mut_modifier ( var : & OutlivedLocal ) -> & ' static str {
968+ if var. mut_usage_outside_body {
969+ "mut "
970+ } else {
971+ ""
972+ }
973+ }
940974 format_to ! ( buf, "{}" , expr) ;
941975 if fun. ret_ty . is_unit ( )
942976 && ( !fun. vars_defined_in_body_and_outlive . is_empty ( ) || !expr. is_block_like ( ) )
@@ -1199,10 +1233,10 @@ fn make_body(
11991233 match fun. vars_defined_in_body_and_outlive . as_slice ( ) {
12001234 [ ] => { }
12011235 [ var] => {
1202- tail_expr = Some ( path_expr_from_local ( ctx, * var) ) ;
1236+ tail_expr = Some ( path_expr_from_local ( ctx, var. local ) ) ;
12031237 }
12041238 vars => {
1205- let exprs = vars. iter ( ) . map ( |var| path_expr_from_local ( ctx, * var) ) ;
1239+ let exprs = vars. iter ( ) . map ( |var| path_expr_from_local ( ctx, var. local ) ) ;
12061240 let expr = make:: expr_tuple ( exprs) ;
12071241 tail_expr = Some ( expr) ;
12081242 }
@@ -2110,6 +2144,30 @@ fn $0fun_name(n: i32) -> i32 {
21102144 ) ;
21112145 }
21122146
2147+ #[ test]
2148+ fn variable_defined_inside_and_used_after_mutably_no_ret ( ) {
2149+ check_assist (
2150+ extract_function,
2151+ r"
2152+ fn foo() {
2153+ let n = 1;
2154+ $0let mut k = n * n;$0
2155+ k += 1;
2156+ }" ,
2157+ r"
2158+ fn foo() {
2159+ let n = 1;
2160+ let mut k = fun_name(n);
2161+ k += 1;
2162+ }
2163+
2164+ fn $0fun_name(n: i32) -> i32 {
2165+ let mut k = n * n;
2166+ k
2167+ }" ,
2168+ ) ;
2169+ }
2170+
21132171 #[ test]
21142172 fn two_variables_defined_inside_and_used_after_no_ret ( ) {
21152173 check_assist (
@@ -2136,6 +2194,38 @@ fn $0fun_name(n: i32) -> (i32, i32) {
21362194 ) ;
21372195 }
21382196
2197+ #[ test]
2198+ fn multi_variables_defined_inside_and_used_after_mutably_no_ret ( ) {
2199+ check_assist (
2200+ extract_function,
2201+ r"
2202+ fn foo() {
2203+ let n = 1;
2204+ $0let mut k = n * n;
2205+ let mut m = k + 2;
2206+ let mut o = m + 3;
2207+ o += 1;$0
2208+ k += o;
2209+ m = 1;
2210+ }" ,
2211+ r"
2212+ fn foo() {
2213+ let n = 1;
2214+ let (mut k, mut m, o) = fun_name(n);
2215+ k += o;
2216+ m = 1;
2217+ }
2218+
2219+ fn $0fun_name(n: i32) -> (i32, i32, i32) {
2220+ let mut k = n * n;
2221+ let mut m = k + 2;
2222+ let mut o = m + 3;
2223+ o += 1;
2224+ (k, m, o)
2225+ }" ,
2226+ ) ;
2227+ }
2228+
21392229 #[ test]
21402230 fn nontrivial_patterns_define_variables ( ) {
21412231 check_assist (
0 commit comments