@@ -75,7 +75,8 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
75
75
let insert_after = scope_for_fn_insertion ( & body, anchor) ?;
76
76
let module = ctx. sema . scope ( & insert_after) . module ( ) ?;
77
77
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) ) ;
79
80
let ret_ty = body_return_ty ( ctx, & body) ?;
80
81
81
82
// FIXME: we compute variables that outlive here just to check `never!` condition
@@ -257,7 +258,7 @@ struct Function {
257
258
control_flow : ControlFlow ,
258
259
ret_ty : RetType ,
259
260
body : FunctionBody ,
260
- vars_defined_in_body_and_outlive : Vec < Local > ,
261
+ vars_defined_in_body_and_outlive : Vec < OutlivedLocal > ,
261
262
}
262
263
263
264
#[ derive( Debug ) ]
@@ -296,9 +297,9 @@ impl Function {
296
297
RetType :: Expr ( ty) => FunType :: Single ( ty. clone ( ) ) ,
297
298
RetType :: Stmt => match self . vars_defined_in_body_and_outlive . as_slice ( ) {
298
299
[ ] => FunType :: Unit ,
299
- [ var] => FunType :: Single ( var. ty ( ctx. db ( ) ) ) ,
300
+ [ var] => FunType :: Single ( var. local . ty ( ctx. db ( ) ) ) ,
300
301
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 ( ) ;
302
303
FunType :: Tuple ( types)
303
304
}
304
305
} ,
@@ -562,6 +563,12 @@ impl HasTokenAtOffset for FunctionBody {
562
563
}
563
564
}
564
565
566
+ #[ derive( Debug ) ]
567
+ struct OutlivedLocal {
568
+ local : Local ,
569
+ mut_usage_outside_body : bool ,
570
+ }
571
+
565
572
/// Try to guess what user wants to extract
566
573
///
567
574
/// We have basically have two cases:
@@ -707,10 +714,10 @@ fn has_exclusive_usages(ctx: &AssistContext, usages: &LocalUsages, body: &Functi
707
714
. any ( |reference| reference_is_exclusive ( reference, body, ctx) )
708
715
}
709
716
710
- /// checks if this reference requires `&mut` access inside body
717
+ /// checks if this reference requires `&mut` access inside node
711
718
fn reference_is_exclusive (
712
719
reference : & FileReference ,
713
- body : & FunctionBody ,
720
+ node : & dyn HasTokenAtOffset ,
714
721
ctx : & AssistContext ,
715
722
) -> bool {
716
723
// we directly modify variable with set: `n = 0`, `n += 1`
@@ -719,7 +726,7 @@ fn reference_is_exclusive(
719
726
}
720
727
721
728
// 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) {
723
730
Some ( path) => path,
724
731
None => return false ,
725
732
} ;
@@ -820,10 +827,16 @@ fn vars_defined_in_body(body: &FunctionBody, ctx: &AssistContext) -> Vec<Local>
820
827
}
821
828
822
829
/// 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) ;
826
836
vars_defined_in_body
837
+ . into_iter ( )
838
+ . filter_map ( |var| var_outlives_body ( ctx, body, var, parent) )
839
+ . collect ( )
827
840
}
828
841
829
842
/// 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 {
843
856
}
844
857
}
845
858
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) ;
849
867
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 } )
851
876
}
852
877
853
878
fn body_return_ty ( ctx : & AssistContext , body : & FunctionBody ) -> Option < RetType > {
@@ -927,16 +952,25 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
927
952
let mut buf = String :: new ( ) ;
928
953
match fun. vars_defined_in_body_and_outlive . as_slice ( ) {
929
954
[ ] => { }
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
+ }
931
958
[ v0, vs @ ..] => {
932
959
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( ) ) ;
934
961
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( ) ) ;
936
963
}
937
964
buf. push_str ( ") = " ) ;
938
965
}
939
966
}
967
+ fn mut_modifier ( var : & OutlivedLocal ) -> & ' static str {
968
+ if var. mut_usage_outside_body {
969
+ "mut "
970
+ } else {
971
+ ""
972
+ }
973
+ }
940
974
format_to ! ( buf, "{}" , expr) ;
941
975
if fun. ret_ty . is_unit ( )
942
976
&& ( !fun. vars_defined_in_body_and_outlive . is_empty ( ) || !expr. is_block_like ( ) )
@@ -1199,10 +1233,10 @@ fn make_body(
1199
1233
match fun. vars_defined_in_body_and_outlive . as_slice ( ) {
1200
1234
[ ] => { }
1201
1235
[ var] => {
1202
- tail_expr = Some ( path_expr_from_local ( ctx, * var) ) ;
1236
+ tail_expr = Some ( path_expr_from_local ( ctx, var. local ) ) ;
1203
1237
}
1204
1238
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 ) ) ;
1206
1240
let expr = make:: expr_tuple ( exprs) ;
1207
1241
tail_expr = Some ( expr) ;
1208
1242
}
@@ -2110,6 +2144,30 @@ fn $0fun_name(n: i32) -> i32 {
2110
2144
) ;
2111
2145
}
2112
2146
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
+
2113
2171
#[ test]
2114
2172
fn two_variables_defined_inside_and_used_after_no_ret ( ) {
2115
2173
check_assist (
@@ -2136,6 +2194,38 @@ fn $0fun_name(n: i32) -> (i32, i32) {
2136
2194
) ;
2137
2195
}
2138
2196
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
+
2139
2229
#[ test]
2140
2230
fn nontrivial_patterns_define_variables ( ) {
2141
2231
check_assist (
0 commit comments