@@ -79,6 +79,35 @@ pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Optio
79
79
)
80
80
}
81
81
82
+ pub ( crate ) fn generate_method ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
83
+ let fn_name: ast:: NameRef = ctx. find_node_at_offset ( ) ?;
84
+ let call: ast:: MethodCallExpr = ctx. find_node_at_offset ( ) ?;
85
+ let module = ctx. sema . scope ( call. syntax ( ) ) . module ( ) ;
86
+ let ty = ctx. sema . type_of_expr ( & call. receiver ( ) ?) ?. as_adt ( ) ?;
87
+
88
+ let function_builder = FunctionBuilder :: from_method_call ( ctx, & call, & fn_name, module) ?;
89
+ let target = call. syntax ( ) . text_range ( ) ;
90
+
91
+ acc. add (
92
+ AssistId ( "generate_method" , AssistKind :: Generate ) ,
93
+ format ! ( "Generate `{}` method" , function_builder. fn_name) ,
94
+ target,
95
+ |builder| {
96
+ let function_template = function_builder. render ( ) ;
97
+ builder. edit_file ( function_template. file ) ;
98
+ let new_fn = format ! (
99
+ "impl {} {{{}}}" ,
100
+ ty. name( ctx. sema. db) ,
101
+ function_template. to_string( ctx. config. snippet_cap)
102
+ ) ;
103
+ match ctx. config . snippet_cap {
104
+ Some ( cap) => builder. insert_snippet ( cap, function_template. insert_offset , new_fn) ,
105
+ None => builder. insert ( function_template. insert_offset , new_fn) ,
106
+ }
107
+ } ,
108
+ )
109
+ }
110
+
82
111
struct FunctionTemplate {
83
112
insert_offset : TextSize ,
84
113
leading_ws : String ,
@@ -181,6 +210,70 @@ impl FunctionBuilder {
181
210
} )
182
211
}
183
212
213
+ fn from_method_call (
214
+ ctx : & AssistContext ,
215
+ call : & ast:: MethodCallExpr ,
216
+ name : & ast:: NameRef ,
217
+ target_module : Option < hir:: Module > ,
218
+ ) -> Option < Self > {
219
+ let mut file = ctx. frange . file_id ;
220
+ let target = match & target_module {
221
+ Some ( target_module) => {
222
+ let module_source = target_module. definition_source ( ctx. db ( ) ) ;
223
+ let ( in_file, target) = next_space_for_fn_in_module ( ctx. sema . db , & module_source) ?;
224
+ file = in_file;
225
+ target
226
+ }
227
+ None => next_space_for_fn_after_method_call_site ( call) ?,
228
+ } ;
229
+ let needs_pub = false ;
230
+ let target_module = target_module. or_else ( || ctx. sema . scope ( target. syntax ( ) ) . module ( ) ) ?;
231
+ let fn_name = make:: name ( & name. text ( ) ) ;
232
+ let ( type_params, params) = method_args ( ctx, target_module, call) ?;
233
+
234
+ let await_expr = call. syntax ( ) . parent ( ) . and_then ( ast:: AwaitExpr :: cast) ;
235
+ let is_async = await_expr. is_some ( ) ;
236
+
237
+ // should_render_snippet intends to express a rough level of confidence about
238
+ // the correctness of the return type.
239
+ //
240
+ // If we are able to infer some return type, and that return type is not unit, we
241
+ // don't want to render the snippet. The assumption here is in this situation the
242
+ // return type is just as likely to be correct as any other part of the generated
243
+ // function.
244
+ //
245
+ // In the case where the return type is inferred as unit it is likely that the
246
+ // user does in fact intend for this generated function to return some non unit
247
+ // type, but that the current state of their code doesn't allow that return type
248
+ // to be accurately inferred.
249
+ let ( ret_ty, should_render_snippet) = {
250
+ match ctx. sema . type_of_expr ( & ast:: Expr :: MethodCallExpr ( call. clone ( ) ) ) {
251
+ Some ( ty) if ty. is_unknown ( ) || ty. is_unit ( ) => ( make:: ty_unit ( ) , true ) ,
252
+ Some ( ty) => {
253
+ let rendered = ty. display_source_code ( ctx. db ( ) , target_module. into ( ) ) ;
254
+ match rendered {
255
+ Ok ( rendered) => ( make:: ty ( & rendered) , false ) ,
256
+ Err ( _) => ( make:: ty_unit ( ) , true ) ,
257
+ }
258
+ }
259
+ None => ( make:: ty_unit ( ) , true ) ,
260
+ }
261
+ } ;
262
+ let ret_type = make:: ret_type ( ret_ty) ;
263
+
264
+ Some ( Self {
265
+ target,
266
+ fn_name,
267
+ type_params,
268
+ params,
269
+ ret_type,
270
+ should_render_snippet,
271
+ file,
272
+ needs_pub,
273
+ is_async,
274
+ } )
275
+ }
276
+
184
277
fn render ( self ) -> FunctionTemplate {
185
278
let placeholder_expr = make:: ext:: expr_todo ( ) ;
186
279
let fn_body = make:: block_expr ( vec ! [ ] , Some ( placeholder_expr) ) ;
@@ -280,6 +373,40 @@ fn fn_args(
280
373
Some ( ( None , make:: param_list ( None , params) ) )
281
374
}
282
375
376
+ fn method_args (
377
+ ctx : & AssistContext ,
378
+ target_module : hir:: Module ,
379
+ call : & ast:: MethodCallExpr ,
380
+ ) -> Option < ( Option < ast:: GenericParamList > , ast:: ParamList ) > {
381
+ let mut arg_names = Vec :: new ( ) ;
382
+ let mut arg_types = Vec :: new ( ) ;
383
+ for arg in call. arg_list ( ) ?. args ( ) {
384
+ arg_names. push ( match fn_arg_name ( & arg) {
385
+ Some ( name) => name,
386
+ None => String :: from ( "arg" ) ,
387
+ } ) ;
388
+ arg_types. push ( match fn_arg_type ( ctx, target_module, & arg) {
389
+ Some ( ty) => {
390
+ if ty. len ( ) > 0 && ty. starts_with ( '&' ) {
391
+ if let Some ( ( new_ty, _) ) = useless_type_special_case ( "" , & ty[ 1 ..] . to_owned ( ) ) {
392
+ new_ty
393
+ } else {
394
+ ty
395
+ }
396
+ } else {
397
+ ty
398
+ }
399
+ }
400
+ None => String :: from ( "()" ) ,
401
+ } ) ;
402
+ }
403
+ deduplicate_arg_names ( & mut arg_names) ;
404
+ let params = arg_names. into_iter ( ) . zip ( arg_types) . map ( |( name, ty) | {
405
+ make:: param ( make:: ext:: simple_ident_pat ( make:: name ( & name) ) . into ( ) , make:: ty ( & ty) )
406
+ } ) ;
407
+ Some ( ( None , make:: param_list ( Some ( make:: self_param ( ) ) , params) ) )
408
+ }
409
+
283
410
/// Makes duplicate argument names unique by appending incrementing numbers.
284
411
///
285
412
/// ```
@@ -368,6 +495,28 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu
368
495
last_ancestor. map ( GeneratedFunctionTarget :: BehindItem )
369
496
}
370
497
498
+ fn next_space_for_fn_after_method_call_site (
499
+ expr : & ast:: MethodCallExpr ,
500
+ ) -> Option < GeneratedFunctionTarget > {
501
+ let mut ancestors = expr. syntax ( ) . ancestors ( ) . peekable ( ) ;
502
+ let mut last_ancestor: Option < SyntaxNode > = None ;
503
+ while let Some ( next_ancestor) = ancestors. next ( ) {
504
+ match next_ancestor. kind ( ) {
505
+ SyntaxKind :: SOURCE_FILE => {
506
+ break ;
507
+ }
508
+ SyntaxKind :: ITEM_LIST => {
509
+ if ancestors. peek ( ) . map ( |a| a. kind ( ) ) == Some ( SyntaxKind :: MODULE ) {
510
+ break ;
511
+ }
512
+ }
513
+ _ => { }
514
+ }
515
+ last_ancestor = Some ( next_ancestor) ;
516
+ }
517
+ last_ancestor. map ( GeneratedFunctionTarget :: BehindItem )
518
+ }
519
+
371
520
fn next_space_for_fn_in_module (
372
521
db : & dyn hir:: db:: AstDatabase ,
373
522
module_source : & hir:: InFile < hir:: ModuleSource > ,
0 commit comments