1
1
use ast:: make;
2
2
use hir:: { db:: HirDatabase , HasSource , PathResolution , Semantics , TypeInfo } ;
3
3
use 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 } ,
5
8
RootDatabase ,
6
9
} ;
7
10
use itertools:: izip;
@@ -54,31 +57,43 @@ use crate::{
54
57
// }
55
58
// ```
56
59
pub ( crate ) fn inline_into_callers ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
60
+ let def_file = ctx. frange . file_id ;
57
61
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
+
62
68
let params = get_fn_params ( ctx. sema . db , function, & param_list) ?;
63
69
64
70
let usages = Definition :: ModuleDef ( hir:: ModuleDef :: Function ( function) ) . usages ( & ctx. sema ) ;
65
71
if !usages. at_least_one ( ) {
66
72
return None ;
67
73
}
68
74
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
+
69
87
acc. add (
70
88
AssistId ( "inline_into_callers" , AssistKind :: RefactorInline ) ,
71
89
"Inline into all callers" ,
72
90
name. syntax ( ) . text_range ( ) ,
73
91
|builder| {
74
- let def_file = ctx. frange . file_id ;
75
- let usages =
76
- Definition :: ModuleDef ( hir:: ModuleDef :: Function ( function) ) . usages ( & ctx. sema ) ;
77
92
let mut usages = usages. all ( ) ;
78
93
let current_file_usage = usages. references . remove ( & def_file) ;
79
94
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 > | {
82
97
builder. edit_file ( file_id) ;
83
98
let count = refs. len ( ) ;
84
99
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
124
139
) ;
125
140
} )
126
141
. count ( ) ;
127
- can_remove &= replaced == count;
142
+ remove_def &= replaced == count;
128
143
} ;
129
144
for ( file_id, refs) in usages. into_iter ( ) {
130
- inline_refs ( file_id, refs) ;
145
+ inline_refs_for_file ( file_id, refs) ;
131
146
}
132
147
if let Some ( refs) = current_file_usage {
133
- inline_refs ( def_file, refs) ;
148
+ inline_refs_for_file ( def_file, refs) ;
134
149
} else {
135
150
builder. edit_file ( def_file) ;
136
151
}
137
- if can_remove {
138
- builder. delete ( func_syn . syntax ( ) . text_range ( ) ) ;
152
+ if remove_def {
153
+ builder. delete ( ast_func . syntax ( ) . text_range ( ) ) ;
139
154
}
140
155
} ,
141
156
)
@@ -201,10 +216,15 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
201
216
)
202
217
} ;
203
218
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 ( ) ?;
207
222
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
+ }
208
228
let params = get_fn_params ( ctx. sema . db , function, & param_list) ?;
209
229
210
230
if call_info. arguments . len ( ) != params. len ( ) {
@@ -220,7 +240,6 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
220
240
label,
221
241
syntax. text_range ( ) ,
222
242
|builder| {
223
- let file_id = file_id. original_file ( ctx. sema . db ) ;
224
243
let replacement = inline ( & ctx. sema , file_id, function, & fn_body, & params, & call_info) ;
225
244
226
245
builder. replace_ast (
@@ -967,6 +986,32 @@ fn foo() {
967
986
foo * 0 + foo
968
987
};
969
988
}
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
+ }
970
1015
"# ,
971
1016
) ;
972
1017
}
0 commit comments