11use ast:: make;
22use hir:: { db:: HirDatabase , HasSource , PathResolution , Semantics , TypeInfo } ;
33use ide_db:: {
4- base_db:: FileId , defs:: Definition , path_transform:: PathTransform , search:: FileReference ,
4+ base_db:: { FileId , FileRange } ,
5+ defs:: Definition ,
6+ path_transform:: PathTransform ,
7+ search:: { FileReference , SearchScope } ,
58 RootDatabase ,
69} ;
710use itertools:: izip;
@@ -54,31 +57,43 @@ use crate::{
5457// }
5558// ```
5659pub ( crate ) fn inline_into_callers ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
60+ let def_file = ctx. frange . file_id ;
5761 let name = ctx. find_node_at_offset :: < ast:: Name > ( ) ?;
58- let func_syn = name. syntax ( ) . parent ( ) . and_then ( ast:: Fn :: cast) ?;
59- let func_body = func_syn. body ( ) ?;
60- let param_list = func_syn. param_list ( ) ?;
61- let function = ctx. sema . to_def ( & func_syn) ?;
62+ let ast_func = name. syntax ( ) . parent ( ) . and_then ( ast:: Fn :: cast) ?;
63+ let func_body = ast_func. body ( ) ?;
64+ let param_list = ast_func. param_list ( ) ?;
65+
66+ let function = ctx. sema . to_def ( & ast_func) ?;
67+
6268 let params = get_fn_params ( ctx. sema . db , function, & param_list) ?;
6369
6470 let usages = Definition :: ModuleDef ( hir:: ModuleDef :: Function ( function) ) . usages ( & ctx. sema ) ;
6571 if !usages. at_least_one ( ) {
6672 return None ;
6773 }
6874
75+ let is_recursive_fn = usages
76+ . clone ( )
77+ . in_scope ( SearchScope :: file_range ( FileRange {
78+ file_id : def_file,
79+ range : func_body. syntax ( ) . text_range ( ) ,
80+ } ) )
81+ . at_least_one ( ) ;
82+ if is_recursive_fn {
83+ cov_mark:: hit!( inline_into_callers_recursive) ;
84+ return None ;
85+ }
86+
6987 acc. add (
7088 AssistId ( "inline_into_callers" , AssistKind :: RefactorInline ) ,
7189 "Inline into all callers" ,
7290 name. syntax ( ) . text_range ( ) ,
7391 |builder| {
74- let def_file = ctx. frange . file_id ;
75- let usages =
76- Definition :: ModuleDef ( hir:: ModuleDef :: Function ( function) ) . usages ( & ctx. sema ) ;
7792 let mut usages = usages. all ( ) ;
7893 let current_file_usage = usages. references . remove ( & def_file) ;
7994
80- let mut can_remove = true ;
81- let mut inline_refs = |file_id, refs : Vec < FileReference > | {
95+ let mut remove_def = true ;
96+ let mut inline_refs_for_file = |file_id, refs : Vec < FileReference > | {
8297 builder. edit_file ( file_id) ;
8398 let count = refs. len ( ) ;
8499 let name_refs = refs. into_iter ( ) . filter_map ( |file_ref| match file_ref. name {
@@ -124,18 +139,18 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext) -> Opt
124139 ) ;
125140 } )
126141 . count ( ) ;
127- can_remove &= replaced == count;
142+ remove_def &= replaced == count;
128143 } ;
129144 for ( file_id, refs) in usages. into_iter ( ) {
130- inline_refs ( file_id, refs) ;
145+ inline_refs_for_file ( file_id, refs) ;
131146 }
132147 if let Some ( refs) = current_file_usage {
133- inline_refs ( def_file, refs) ;
148+ inline_refs_for_file ( def_file, refs) ;
134149 } else {
135150 builder. edit_file ( def_file) ;
136151 }
137- if can_remove {
138- builder. delete ( func_syn . syntax ( ) . text_range ( ) ) ;
152+ if remove_def {
153+ builder. delete ( ast_func . syntax ( ) . text_range ( ) ) ;
139154 }
140155 } ,
141156 )
@@ -201,10 +216,15 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
201216 )
202217 } ;
203218
204- let hir :: InFile { value : function_source , file_id } = function. source ( ctx. db ( ) ) ?;
205- let fn_body = function_source . body ( ) ?;
206- let param_list = function_source . param_list ( ) ?;
219+ let fn_source = function. source ( ctx. db ( ) ) ?;
220+ let fn_body = fn_source . value . body ( ) ?;
221+ let param_list = fn_source . value . param_list ( ) ?;
207222
223+ let FileRange { file_id, range } = fn_source. syntax ( ) . original_file_range ( ctx. sema . db ) ;
224+ if file_id == ctx. frange . file_id && range. contains ( ctx. frange . range . start ( ) ) {
225+ cov_mark:: hit!( inline_call_recursive) ;
226+ return None ;
227+ }
208228 let params = get_fn_params ( ctx. sema . db , function, & param_list) ?;
209229
210230 if call_info. arguments . len ( ) != params. len ( ) {
@@ -220,7 +240,6 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
220240 label,
221241 syntax. text_range ( ) ,
222242 |builder| {
223- let file_id = file_id. original_file ( ctx. sema . db ) ;
224243 let replacement = inline ( & ctx. sema , file_id, function, & fn_body, & params, & call_info) ;
225244
226245 builder. replace_ast (
@@ -967,6 +986,32 @@ fn foo() {
967986 foo * 0 + foo
968987 };
969988}
989+ "# ,
990+ ) ;
991+ }
992+
993+ #[ test]
994+ fn inline_callers_recursive ( ) {
995+ cov_mark:: check!( inline_into_callers_recursive) ;
996+ check_assist_not_applicable (
997+ inline_into_callers,
998+ r#"
999+ fn foo$0() {
1000+ foo();
1001+ }
1002+ "# ,
1003+ ) ;
1004+ }
1005+
1006+ #[ test]
1007+ fn inline_call_recursive ( ) {
1008+ cov_mark:: check!( inline_call_recursive) ;
1009+ check_assist_not_applicable (
1010+ inline_call,
1011+ r#"
1012+ fn foo() {
1013+ foo$0();
1014+ }
9701015"# ,
9711016 ) ;
9721017 }
0 commit comments