@@ -13,8 +13,11 @@ use ide_db::{
13
13
} ;
14
14
use itertools:: Itertools ;
15
15
use std:: fmt:: Write ;
16
- use stdx:: { always, never} ;
17
- use syntax:: { AstNode , SyntaxKind , SyntaxNode , TextRange , TextSize , ast} ;
16
+ use stdx:: { always, format_to, never} ;
17
+ use syntax:: {
18
+ AstNode , SyntaxKind , SyntaxNode , TextRange , TextSize ,
19
+ ast:: { self , HasArgList , prec:: ExprPrecedence } ,
20
+ } ;
18
21
19
22
use ide_db:: text_edit:: TextEdit ;
20
23
@@ -331,6 +334,85 @@ fn find_definitions(
331
334
}
332
335
}
333
336
337
+ fn transform_assoc_fn_into_method_call (
338
+ sema : & Semantics < ' _ , RootDatabase > ,
339
+ source_change : & mut SourceChange ,
340
+ f : hir:: Function ,
341
+ ) {
342
+ let calls = Definition :: Function ( f) . usages ( sema) . all ( ) ;
343
+ for ( file_id, calls) in calls {
344
+ for call in calls {
345
+ let Some ( fn_name) = call. name . as_name_ref ( ) else { continue } ;
346
+ let Some ( path) = fn_name. syntax ( ) . parent ( ) . and_then ( ast:: PathSegment :: cast) else {
347
+ continue ;
348
+ } ;
349
+ let path = path. parent_path ( ) ;
350
+ // The `PathExpr` is the direct parent, above it is the `CallExpr`.
351
+ let Some ( call) =
352
+ path. syntax ( ) . parent ( ) . and_then ( |it| ast:: CallExpr :: cast ( it. parent ( ) ?) )
353
+ else {
354
+ continue ;
355
+ } ;
356
+
357
+ let Some ( arg_list) = call. arg_list ( ) else { continue } ;
358
+ let mut args = arg_list. args ( ) ;
359
+ let Some ( mut self_arg) = args. next ( ) else { continue } ;
360
+ let second_arg = args. next ( ) ;
361
+
362
+ // Strip (de)references, as they will be taken automatically by auto(de)ref.
363
+ loop {
364
+ let self_ = match & self_arg {
365
+ ast:: Expr :: RefExpr ( self_) => self_. expr ( ) ,
366
+ ast:: Expr :: ParenExpr ( self_) => self_. expr ( ) ,
367
+ ast:: Expr :: PrefixExpr ( self_)
368
+ if self_. op_kind ( ) == Some ( ast:: UnaryOp :: Deref ) =>
369
+ {
370
+ self_. expr ( )
371
+ }
372
+ _ => break ,
373
+ } ;
374
+ self_arg = match self_ {
375
+ Some ( it) => it,
376
+ None => break ,
377
+ } ;
378
+ }
379
+
380
+ let self_needs_parens =
381
+ self_arg. precedence ( ) . needs_parentheses_in ( ExprPrecedence :: Postfix ) ;
382
+
383
+ let replace_start = path. syntax ( ) . text_range ( ) . start ( ) ;
384
+ let replace_end = match second_arg {
385
+ Some ( second_arg) => second_arg. syntax ( ) . text_range ( ) . start ( ) ,
386
+ None => arg_list
387
+ . r_paren_token ( )
388
+ . map ( |it| it. text_range ( ) . start ( ) )
389
+ . unwrap_or_else ( || arg_list. syntax ( ) . text_range ( ) . end ( ) ) ,
390
+ } ;
391
+ let replace_range = TextRange :: new ( replace_start, replace_end) ;
392
+
393
+ let Some ( macro_mapped_self) = sema. original_range_opt ( self_arg. syntax ( ) ) else {
394
+ continue ;
395
+ } ;
396
+ let mut replacement = String :: new ( ) ;
397
+ if self_needs_parens {
398
+ replacement. push ( '(' ) ;
399
+ }
400
+ replacement. push_str ( macro_mapped_self. text ( sema. db ) ) ;
401
+ if self_needs_parens {
402
+ replacement. push ( ')' ) ;
403
+ }
404
+ replacement. push ( '.' ) ;
405
+ format_to ! ( replacement, "{fn_name}" ) ;
406
+ replacement. push ( '(' ) ;
407
+
408
+ source_change. insert_source_edit (
409
+ file_id. file_id ( sema. db ) ,
410
+ TextEdit :: replace ( replace_range, replacement) ,
411
+ ) ;
412
+ }
413
+ }
414
+ }
415
+
334
416
fn rename_to_self (
335
417
sema : & Semantics < ' _ , RootDatabase > ,
336
418
local : hir:: Local ,
@@ -408,6 +490,7 @@ fn rename_to_self(
408
490
file_id. original_file ( sema. db ) . file_id ( sema. db ) ,
409
491
TextEdit :: replace ( param_source. syntax ( ) . text_range ( ) , String :: from ( self_param) ) ,
410
492
) ;
493
+ transform_assoc_fn_into_method_call ( sema, & mut source_change, fn_def) ;
411
494
Ok ( source_change)
412
495
}
413
496
@@ -3412,4 +3495,78 @@ fn other_place() { Quux::Bar$0; }
3412
3495
"# ,
3413
3496
) ;
3414
3497
}
3498
+
3499
+ #[ test]
3500
+ fn rename_to_self_callers ( ) {
3501
+ check (
3502
+ "self" ,
3503
+ r#"
3504
+ //- minicore: add
3505
+ struct Foo;
3506
+ impl core::ops::Add for Foo {
3507
+ type Target = Foo;
3508
+ fn add(self, _: Self) -> Foo { Foo }
3509
+ }
3510
+
3511
+ impl Foo {
3512
+ fn foo(th$0is: &Self) {}
3513
+ }
3514
+
3515
+ fn bar(v: &Foo) {
3516
+ Foo::foo(v);
3517
+ }
3518
+
3519
+ fn baz() {
3520
+ Foo::foo(&Foo);
3521
+ Foo::foo(Foo + Foo);
3522
+ }
3523
+ "# ,
3524
+ r#"
3525
+ struct Foo;
3526
+ impl core::ops::Add for Foo {
3527
+ type Target = Foo;
3528
+ fn add(self, _: Self) -> Foo { Foo }
3529
+ }
3530
+
3531
+ impl Foo {
3532
+ fn foo(&self) {}
3533
+ }
3534
+
3535
+ fn bar(v: &Foo) {
3536
+ v.foo();
3537
+ }
3538
+
3539
+ fn baz() {
3540
+ Foo.foo();
3541
+ (Foo + Foo).foo();
3542
+ }
3543
+ "# ,
3544
+ ) ;
3545
+ // Multiple arguments:
3546
+ check (
3547
+ "self" ,
3548
+ r#"
3549
+ struct Foo;
3550
+
3551
+ impl Foo {
3552
+ fn foo(th$0is: &Self, v: i32) {}
3553
+ }
3554
+
3555
+ fn bar(v: Foo) {
3556
+ Foo::foo(&v, 123);
3557
+ }
3558
+ "# ,
3559
+ r#"
3560
+ struct Foo;
3561
+
3562
+ impl Foo {
3563
+ fn foo(&self, v: i32) {}
3564
+ }
3565
+
3566
+ fn bar(v: Foo) {
3567
+ v.foo(123);
3568
+ }
3569
+ "# ,
3570
+ ) ;
3571
+ }
3415
3572
}
0 commit comments