@@ -13,8 +13,11 @@ use ide_db::{
1313} ;
1414use itertools:: Itertools ;
1515use 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+ } ;
1821
1922use ide_db:: text_edit:: TextEdit ;
2023
@@ -331,6 +334,85 @@ fn find_definitions(
331334 }
332335}
333336
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+
334416fn rename_to_self (
335417 sema : & Semantics < ' _ , RootDatabase > ,
336418 local : hir:: Local ,
@@ -408,6 +490,7 @@ fn rename_to_self(
408490 file_id. original_file ( sema. db ) . file_id ( sema. db ) ,
409491 TextEdit :: replace ( param_source. syntax ( ) . text_range ( ) , String :: from ( self_param) ) ,
410492 ) ;
493+ transform_assoc_fn_into_method_call ( sema, & mut source_change, fn_def) ;
411494 Ok ( source_change)
412495}
413496
@@ -3412,4 +3495,78 @@ fn other_place() { Quux::Bar$0; }
34123495"# ,
34133496 ) ;
34143497 }
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+ }
34153572}
0 commit comments