1
1
use ast:: make;
2
2
use hir:: { HasSource , PathResolution } ;
3
+ use ide_db:: { defs:: Definition , search:: FileReference } ;
4
+ use itertools:: izip;
3
5
use syntax:: {
4
6
ast:: { self , edit:: AstNodeEdit , ArgListOwner } ,
5
7
ted, AstNode ,
@@ -69,23 +71,25 @@ pub(crate) fn inline_(
69
71
arg_list : Vec < ast:: Expr > ,
70
72
expr : ast:: Expr ,
71
73
) -> Option < ( ) > {
72
- let hir:: InFile { value : function_source, .. } = function. source ( ctx. db ( ) ) ?;
74
+ let hir:: InFile { value : function_source, file_id } = function. source ( ctx. db ( ) ) ?;
73
75
let param_list = function_source. param_list ( ) ?;
76
+ let mut assoc_fn_params = function. assoc_fn_params ( ctx. sema . db ) . into_iter ( ) ;
74
77
75
78
let mut params = Vec :: new ( ) ;
76
79
if let Some ( self_param) = param_list. self_param ( ) {
77
80
// FIXME this should depend on the receiver as well as the self_param
78
- params. push (
81
+ params. push ( (
79
82
make:: ident_pat (
80
83
self_param. amp_token ( ) . is_some ( ) ,
81
84
self_param. mut_token ( ) . is_some ( ) ,
82
85
make:: name ( "this" ) ,
83
86
)
84
87
. into ( ) ,
85
- ) ;
88
+ assoc_fn_params. next ( ) ?,
89
+ ) ) ;
86
90
}
87
91
for param in param_list. params ( ) {
88
- params. push ( param. pat ( ) ?) ;
92
+ params. push ( ( param. pat ( ) ?, assoc_fn_params . next ( ) ? ) ) ;
89
93
}
90
94
91
95
if arg_list. len ( ) != params. len ( ) {
@@ -95,41 +99,95 @@ pub(crate) fn inline_(
95
99
return None ;
96
100
}
97
101
98
- let new_bindings = params. into_iter ( ) . zip ( arg_list) ;
99
-
100
102
let body = function_source. body ( ) ?;
101
103
102
104
acc. add (
103
105
AssistId ( "inline_call" , AssistKind :: RefactorInline ) ,
104
106
label,
105
107
expr. syntax ( ) . text_range ( ) ,
106
108
|builder| {
107
- // FIXME: emit type ascriptions when a coercion happens?
108
- // FIXME: dont create locals when its not required
109
- let statements = new_bindings
110
- . map ( |( pattern, value) | make:: let_stmt ( pattern, Some ( value) ) . into ( ) )
111
- . chain ( body. statements ( ) ) ;
112
-
113
- let original_indentation = expr. indent_level ( ) ;
114
- let mut replacement = make:: block_expr ( statements, body. tail_expr ( ) )
115
- . reset_indent ( )
116
- . indent ( original_indentation) ;
117
-
109
+ let body = body. clone_for_update ( ) ;
110
+
111
+ let file_id = file_id. original_file ( ctx. sema . db ) ;
112
+ let usages_for_locals = |local| {
113
+ Definition :: Local ( local)
114
+ . usages ( & ctx. sema )
115
+ . all ( )
116
+ . references
117
+ . remove ( & file_id)
118
+ . unwrap_or_default ( )
119
+ . into_iter ( )
120
+ } ;
121
+ // Contains the nodes of usages of parameters.
122
+ // If the inner Vec for a parameter is empty it either means there are no usages or that the parameter
123
+ // has a pattern that does not allow inlining
124
+ let param_use_nodes: Vec < Vec < _ > > = params
125
+ . iter ( )
126
+ . map ( |( pat, param) | {
127
+ if !matches ! ( pat, ast:: Pat :: IdentPat ( pat) if pat. is_simple_ident( ) ) {
128
+ return Vec :: new ( ) ;
129
+ }
130
+ usages_for_locals ( param. as_local ( ctx. sema . db ) )
131
+ . map ( |FileReference { name, range, .. } | match name {
132
+ ast:: NameLike :: NameRef ( _) => body
133
+ . syntax ( )
134
+ . covering_element ( range)
135
+ . ancestors ( )
136
+ . nth ( 3 )
137
+ . filter ( |it| ast:: PathExpr :: can_cast ( it. kind ( ) ) ) ,
138
+ _ => None ,
139
+ } )
140
+ . collect :: < Option < Vec < _ > > > ( )
141
+ . unwrap_or_default ( )
142
+ } )
143
+ . collect ( ) ;
144
+
145
+ // Rewrite `self` to `this`
118
146
if param_list. self_param ( ) . is_some ( ) {
119
- replacement = replacement. clone_for_update ( ) ;
120
147
let this = make:: name_ref ( "this" ) . syntax ( ) . clone_for_update ( ) ;
121
- // FIXME dont look into descendant methods
122
- replacement
123
- . syntax ( )
124
- . descendants ( )
125
- . filter_map ( ast:: NameRef :: cast)
126
- . filter ( |n| n. self_token ( ) . is_some ( ) )
127
- . collect :: < Vec < _ > > ( )
128
- . into_iter ( )
129
- . rev ( )
130
- . for_each ( |self_ref| ted:: replace ( self_ref. syntax ( ) , & this) ) ;
148
+ usages_for_locals ( params[ 0 ] . 1 . as_local ( ctx. sema . db ) )
149
+ . flat_map ( |FileReference { name, range, .. } | match name {
150
+ ast:: NameLike :: NameRef ( _) => Some ( body. syntax ( ) . covering_element ( range) ) ,
151
+ _ => None ,
152
+ } )
153
+ . for_each ( |it| {
154
+ ted:: replace ( it, & this) ;
155
+ } )
156
+ }
157
+
158
+ // Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
159
+ for ( ( pat, _) , usages, expr) in izip ! ( params, param_use_nodes, arg_list) . rev ( ) {
160
+ match & * usages {
161
+ // inline single use parameters
162
+ [ usage] => {
163
+ ted:: replace ( usage, expr. syntax ( ) . clone_for_update ( ) ) ;
164
+ }
165
+ // inline parameters whose expression is a simple local reference
166
+ [ _, ..]
167
+ if matches ! ( & expr,
168
+ ast:: Expr :: PathExpr ( expr)
169
+ if expr. path( ) . and_then( |path| path. as_single_name_ref( ) ) . is_some( )
170
+ ) =>
171
+ {
172
+ let expr = expr. syntax ( ) . clone_for_update ( ) ;
173
+ usages. into_iter ( ) . for_each ( |usage| {
174
+ ted:: replace ( usage, & expr) ;
175
+ } ) ;
176
+ }
177
+ // cant inline, emit a let statement
178
+ // FIXME: emit type ascriptions when a coercion happens?
179
+ _ => body. push_front ( make:: let_stmt ( pat, Some ( expr) ) . clone_for_update ( ) . into ( ) ) ,
180
+ }
131
181
}
132
- builder. replace_ast ( expr, ast:: Expr :: BlockExpr ( replacement) ) ;
182
+
183
+ let original_indentation = expr. indent_level ( ) ;
184
+ let replacement = body. reset_indent ( ) . indent ( original_indentation) ;
185
+
186
+ let replacement = match replacement. tail_expr ( ) {
187
+ Some ( expr) if replacement. statements ( ) . next ( ) . is_none ( ) => expr,
188
+ _ => ast:: Expr :: BlockExpr ( replacement) ,
189
+ } ;
190
+ builder. replace_ast ( expr, replacement) ;
133
191
} ,
134
192
)
135
193
}
@@ -153,9 +211,7 @@ fn main() {
153
211
r#"
154
212
fn foo() { println!("Hello, World!"); }
155
213
fn main() {
156
- {
157
- println!("Hello, World!");
158
- };
214
+ { println!("Hello, World!"); };
159
215
}
160
216
"# ,
161
217
) ;
@@ -174,10 +230,7 @@ fn main() {
174
230
r#"
175
231
fn foo(name: String) { println!("Hello, {}!", name); }
176
232
fn main() {
177
- {
178
- let name = String::from("Michael");
179
- println!("Hello, {}!", name);
180
- };
233
+ { let name = String::from("Michael"); println!("Hello, {}!", name); };
181
234
}
182
235
"# ,
183
236
) ;
@@ -218,10 +271,8 @@ fn foo(a: u32, b: u32) -> u32 {
218
271
}
219
272
220
273
fn main() {
221
- let x = {
222
- let a = 1;
223
- let b = 2;
224
- let x = a + b;
274
+ let x = { let b = 2;
275
+ let x = 1 + b;
225
276
let y = x - b;
226
277
x * y
227
278
};
@@ -257,11 +308,7 @@ impl Foo {
257
308
}
258
309
259
310
fn main() {
260
- let x = {
261
- let this = Foo(3);
262
- let a = 2;
263
- Foo(this.0 + a)
264
- };
311
+ let x = Foo(Foo(3).0 + 2);
265
312
}
266
313
"# ,
267
314
) ;
@@ -294,11 +341,7 @@ impl Foo {
294
341
}
295
342
296
343
fn main() {
297
- let x = {
298
- let this = Foo(3);
299
- let a = 2;
300
- Foo(this.0 + a)
301
- };
344
+ let x = Foo(Foo(3).0 + 2);
302
345
}
303
346
"# ,
304
347
) ;
@@ -331,10 +374,8 @@ impl Foo {
331
374
}
332
375
333
376
fn main() {
334
- let x = {
335
- let ref this = Foo(3);
336
- let a = 2;
337
- Foo(this.0 + a)
377
+ let x = { let ref this = Foo(3);
378
+ Foo(this.0 + 2)
338
379
};
339
380
}
340
381
"# ,
@@ -370,8 +411,7 @@ impl Foo {
370
411
371
412
fn main() {
372
413
let mut foo = Foo(3);
373
- {
374
- let ref mut this = foo;
414
+ { let ref mut this = foo;
375
415
this.0 = 0;
376
416
};
377
417
}
0 commit comments