@@ -96,7 +96,8 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
96
96
return ;
97
97
}
98
98
99
- let params = body. extracted_function_params ( ctx, locals_used. iter ( ) . copied ( ) ) ;
99
+ let params =
100
+ body. extracted_function_params ( ctx, & container_info, locals_used. iter ( ) . copied ( ) ) ;
100
101
101
102
let fun = Function {
102
103
name : "fun_name" . to_string ( ) ,
@@ -183,8 +184,8 @@ struct Function {
183
184
struct Param {
184
185
var : Local ,
185
186
ty : hir:: Type ,
186
- has_usages_afterwards : bool ,
187
- has_mut_inside_body : bool ,
187
+ move_local : bool ,
188
+ requires_mut : bool ,
188
189
is_copy : bool ,
189
190
}
190
191
@@ -226,6 +227,7 @@ struct ControlFlow {
226
227
struct ContainerInfo {
227
228
is_const : bool ,
228
229
is_in_tail : bool ,
230
+ parent_loop : Option < SyntaxNode > ,
229
231
/// The function's return type, const's type etc.
230
232
ret_type : Option < hir:: Type > ,
231
233
}
@@ -335,11 +337,11 @@ impl ParamKind {
335
337
336
338
impl Param {
337
339
fn kind ( & self ) -> ParamKind {
338
- match ( self . has_usages_afterwards , self . has_mut_inside_body , self . is_copy ) {
339
- ( true , true , _) => ParamKind :: MutRef ,
340
- ( true , false , false ) => ParamKind :: SharedRef ,
341
- ( false , true , _) => ParamKind :: MutValue ,
342
- ( true , false , true ) | ( false , false , _) => ParamKind :: Value ,
340
+ match ( self . move_local , self . requires_mut , self . is_copy ) {
341
+ ( false , true , _) => ParamKind :: MutRef ,
342
+ ( false , false , false ) => ParamKind :: SharedRef ,
343
+ ( true , true , _) => ParamKind :: MutValue ,
344
+ ( _ , false , _) => ParamKind :: Value ,
343
345
}
344
346
}
345
347
@@ -622,6 +624,15 @@ impl FunctionBody {
622
624
fn analyze_container ( & self , sema : & Semantics < RootDatabase > ) -> Option < ContainerInfo > {
623
625
let mut ancestors = self . parent ( ) ?. ancestors ( ) ;
624
626
let infer_expr_opt = |expr| sema. type_of_expr ( & expr?) . map ( TypeInfo :: adjusted) ;
627
+ let mut parent_loop = None ;
628
+ let mut set_parent_loop = |loop_ : & dyn ast:: LoopBodyOwner | {
629
+ if loop_
630
+ . loop_body ( )
631
+ . map_or ( false , |it| it. syntax ( ) . text_range ( ) . contains_range ( self . text_range ( ) ) )
632
+ {
633
+ parent_loop. get_or_insert ( loop_. syntax ( ) . clone ( ) ) ;
634
+ }
635
+ } ;
625
636
let ( is_const, expr, ty) = loop {
626
637
let anc = ancestors. next ( ) ?;
627
638
break match_ast ! {
@@ -658,6 +669,18 @@ impl FunctionBody {
658
669
} ,
659
670
ast:: Variant ( __) => return None ,
660
671
ast:: Meta ( __) => return None ,
672
+ ast:: LoopExpr ( it) => {
673
+ set_parent_loop( & it) ;
674
+ continue ;
675
+ } ,
676
+ ast:: ForExpr ( it) => {
677
+ set_parent_loop( & it) ;
678
+ continue ;
679
+ } ,
680
+ ast:: WhileExpr ( it) => {
681
+ set_parent_loop( & it) ;
682
+ continue ;
683
+ } ,
661
684
_ => continue ,
662
685
}
663
686
} ;
@@ -670,7 +693,7 @@ impl FunctionBody {
670
693
container_tail. zip ( self . tail_expr ( ) ) . map_or ( false , |( container_tail, body_tail) | {
671
694
container_tail. syntax ( ) . text_range ( ) . contains_range ( body_tail. syntax ( ) . text_range ( ) )
672
695
} ) ;
673
- Some ( ContainerInfo { is_in_tail, is_const, ret_type : ty } )
696
+ Some ( ContainerInfo { is_in_tail, is_const, parent_loop , ret_type : ty } )
674
697
}
675
698
676
699
fn return_ty ( & self , ctx : & AssistContext ) -> Option < RetType > {
@@ -780,34 +803,38 @@ impl FunctionBody {
780
803
781
804
Some ( ControlFlow { kind, is_async, is_unsafe : _is_unsafe } )
782
805
}
806
+
783
807
/// find variables that should be extracted as params
784
808
///
785
809
/// Computes additional info that affects param type and mutability
786
810
fn extracted_function_params (
787
811
& self ,
788
812
ctx : & AssistContext ,
813
+ container_info : & ContainerInfo ,
789
814
locals : impl Iterator < Item = Local > ,
790
815
) -> Vec < Param > {
791
816
locals
792
817
. map ( |local| ( local, local. source ( ctx. db ( ) ) ) )
793
818
. filter ( |( _, src) | is_defined_outside_of_body ( ctx, self , src) )
794
819
. filter_map ( |( local, src) | {
795
- if src. value . is_left ( ) {
796
- Some ( local)
820
+ if let Either :: Left ( src) = src . value {
821
+ Some ( ( local, src ) )
797
822
} else {
798
823
stdx:: never!( false , "Local::is_self returned false, but source is SelfParam" ) ;
799
824
None
800
825
}
801
826
} )
802
- . map ( |var| {
827
+ . map ( |( var, src ) | {
803
828
let usages = LocalUsages :: find_local_usages ( ctx, var) ;
804
829
let ty = var. ty ( ctx. db ( ) ) ;
805
830
let is_copy = ty. is_copy ( ctx. db ( ) ) ;
806
831
Param {
807
832
var,
808
833
ty,
809
- has_usages_afterwards : self . has_usages_after_body ( & usages) ,
810
- has_mut_inside_body : has_exclusive_usages ( ctx, & usages, self ) ,
834
+ move_local : container_info. parent_loop . as_ref ( ) . map_or ( true , |it| {
835
+ it. text_range ( ) . contains_range ( src. syntax ( ) . text_range ( ) )
836
+ } ) && !self . has_usages_after_body ( & usages) ,
837
+ requires_mut : has_exclusive_usages ( ctx, & usages, self ) ,
811
838
is_copy,
812
839
}
813
840
} )
@@ -4009,6 +4036,83 @@ const FOO: () = {
4009
4036
const fn $0fun_name() {
4010
4037
()
4011
4038
}
4039
+ "# ,
4040
+ ) ;
4041
+ }
4042
+
4043
+ #[ test]
4044
+ fn extract_does_not_move_outer_loop_vars ( ) {
4045
+ check_assist (
4046
+ extract_function,
4047
+ r#"
4048
+ fn foo() {
4049
+ let mut x = 5;
4050
+ for _ in 0..10 {
4051
+ $0x += 1;$0
4052
+ }
4053
+ }
4054
+ "# ,
4055
+ r#"
4056
+ fn foo() {
4057
+ let mut x = 5;
4058
+ for _ in 0..10 {
4059
+ fun_name(&mut x);
4060
+ }
4061
+ }
4062
+
4063
+ fn $0fun_name(x: &mut i32) {
4064
+ *x += 1;
4065
+ }
4066
+ "# ,
4067
+ ) ;
4068
+ check_assist (
4069
+ extract_function,
4070
+ r#"
4071
+ fn foo() {
4072
+ for _ in 0..10 {
4073
+ let mut x = 5;
4074
+ $0x += 1;$0
4075
+ }
4076
+ }
4077
+ "# ,
4078
+ r#"
4079
+ fn foo() {
4080
+ for _ in 0..10 {
4081
+ let mut x = 5;
4082
+ fun_name(x);
4083
+ }
4084
+ }
4085
+
4086
+ fn $0fun_name(mut x: i32) {
4087
+ x += 1;
4088
+ }
4089
+ "# ,
4090
+ ) ;
4091
+ check_assist (
4092
+ extract_function,
4093
+ r#"
4094
+ fn foo() {
4095
+ loop {
4096
+ let mut x = 5;
4097
+ for _ in 0..10 {
4098
+ $0x += 1;$0
4099
+ }
4100
+ }
4101
+ }
4102
+ "# ,
4103
+ r#"
4104
+ fn foo() {
4105
+ loop {
4106
+ let mut x = 5;
4107
+ for _ in 0..10 {
4108
+ fun_name(&mut x);
4109
+ }
4110
+ }
4111
+ }
4112
+
4113
+ fn $0fun_name(x: &mut i32) {
4114
+ *x += 1;
4115
+ }
4012
4116
"# ,
4013
4117
) ;
4014
4118
}
0 commit comments