@@ -96,7 +96,8 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
9696 return ;
9797 }
9898
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 ( ) ) ;
100101
101102 let fun = Function {
102103 name : "fun_name" . to_string ( ) ,
@@ -183,8 +184,8 @@ struct Function {
183184struct Param {
184185 var : Local ,
185186 ty : hir:: Type ,
186- has_usages_afterwards : bool ,
187- has_mut_inside_body : bool ,
187+ move_local : bool ,
188+ requires_mut : bool ,
188189 is_copy : bool ,
189190}
190191
@@ -226,6 +227,7 @@ struct ControlFlow {
226227struct ContainerInfo {
227228 is_const : bool ,
228229 is_in_tail : bool ,
230+ parent_loop : Option < SyntaxNode > ,
229231 /// The function's return type, const's type etc.
230232 ret_type : Option < hir:: Type > ,
231233}
@@ -335,11 +337,11 @@ impl ParamKind {
335337
336338impl Param {
337339 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 ,
343345 }
344346 }
345347
@@ -622,6 +624,15 @@ impl FunctionBody {
622624 fn analyze_container ( & self , sema : & Semantics < RootDatabase > ) -> Option < ContainerInfo > {
623625 let mut ancestors = self . parent ( ) ?. ancestors ( ) ;
624626 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+ } ;
625636 let ( is_const, expr, ty) = loop {
626637 let anc = ancestors. next ( ) ?;
627638 break match_ast ! {
@@ -658,6 +669,18 @@ impl FunctionBody {
658669 } ,
659670 ast:: Variant ( __) => return None ,
660671 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+ } ,
661684 _ => continue ,
662685 }
663686 } ;
@@ -670,7 +693,7 @@ impl FunctionBody {
670693 container_tail. zip ( self . tail_expr ( ) ) . map_or ( false , |( container_tail, body_tail) | {
671694 container_tail. syntax ( ) . text_range ( ) . contains_range ( body_tail. syntax ( ) . text_range ( ) )
672695 } ) ;
673- Some ( ContainerInfo { is_in_tail, is_const, ret_type : ty } )
696+ Some ( ContainerInfo { is_in_tail, is_const, parent_loop , ret_type : ty } )
674697 }
675698
676699 fn return_ty ( & self , ctx : & AssistContext ) -> Option < RetType > {
@@ -780,34 +803,38 @@ impl FunctionBody {
780803
781804 Some ( ControlFlow { kind, is_async, is_unsafe : _is_unsafe } )
782805 }
806+
783807 /// find variables that should be extracted as params
784808 ///
785809 /// Computes additional info that affects param type and mutability
786810 fn extracted_function_params (
787811 & self ,
788812 ctx : & AssistContext ,
813+ container_info : & ContainerInfo ,
789814 locals : impl Iterator < Item = Local > ,
790815 ) -> Vec < Param > {
791816 locals
792817 . map ( |local| ( local, local. source ( ctx. db ( ) ) ) )
793818 . filter ( |( _, src) | is_defined_outside_of_body ( ctx, self , src) )
794819 . 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 ) )
797822 } else {
798823 stdx:: never!( false , "Local::is_self returned false, but source is SelfParam" ) ;
799824 None
800825 }
801826 } )
802- . map ( |var| {
827+ . map ( |( var, src ) | {
803828 let usages = LocalUsages :: find_local_usages ( ctx, var) ;
804829 let ty = var. ty ( ctx. db ( ) ) ;
805830 let is_copy = ty. is_copy ( ctx. db ( ) ) ;
806831 Param {
807832 var,
808833 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 ) ,
811838 is_copy,
812839 }
813840 } )
@@ -4009,6 +4036,83 @@ const FOO: () = {
40094036const fn $0fun_name() {
40104037 ()
40114038}
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+ }
40124116"# ,
40134117 ) ;
40144118 }
0 commit comments