about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-02 10:53:35 +0000
committerbors <bors@rust-lang.org>2024-01-02 10:53:35 +0000
commit306defaef495eff0f445b8a922e6e155dfb50e4e (patch)
tree627aac3f5f703d0a9512bc9feba53da33350cbaf
parent792c94621d7d394fc631231f93f0cee55ffd00a4 (diff)
parent38b5dcb53c33f7852071b4744919279a7970f1c5 (diff)
downloadrust-306defaef495eff0f445b8a922e6e155dfb50e4e.tar.gz
rust-306defaef495eff0f445b8a922e6e155dfb50e4e.zip
Auto merge of #16114 - roife:fix-inline-with-self-type, r=Veykril
fix: self type replacement in inline-function

Fix #16113, fix #16091

The problem described in this issue actually involves three bugs.

Firstly, when using `ted` to modify the syntax tree, the offset of nodes on the tree changes, which causes the syntax range information from `hir` to become invalid. Therefore, we need to edit the AST after the last usage for `usages_for_locals`.

The second issue is that when inserting nodes, it's necessary to use `clone_subtree` for duplication because the `ted::replace` operation essentially moves a node.

The third issue is that we should use `ancestors_with_macros` instead of `ancestors` to handle impl definition in macros.

I have fixed the three bugs mentioned above and added unit tests.
-rw-r--r--crates/ide-assists/src/handlers/inline_call.rs134
1 files changed, 123 insertions, 11 deletions
diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs
index 5b9cc5f66cd..2eb7089b7c3 100644
--- a/crates/ide-assists/src/handlers/inline_call.rs
+++ b/crates/ide-assists/src/handlers/inline_call.rs
@@ -315,17 +315,6 @@ fn inline(
     } else {
         fn_body.clone_for_update()
     };
-    if let Some(imp) = body.syntax().ancestors().find_map(ast::Impl::cast) {
-        if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) {
-            if let Some(t) = imp.self_ty() {
-                body.syntax()
-                    .descendants_with_tokens()
-                    .filter_map(NodeOrToken::into_token)
-                    .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
-                    .for_each(|tok| ted::replace(tok, t.syntax()));
-            }
-        }
-    }
     let usages_for_locals = |local| {
         Definition::Local(local)
             .usages(sema)
@@ -381,6 +370,27 @@ fn inline(
         }
     }
 
+    // We should place the following code after last usage of `usages_for_locals`
+    // because `ted::replace` will change the offset in syntax tree, which makes
+    // `FileReference` incorrect
+    if let Some(imp) =
+        sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast)
+    {
+        if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) {
+            if let Some(t) = imp.self_ty() {
+                while let Some(self_tok) = body
+                    .syntax()
+                    .descendants_with_tokens()
+                    .filter_map(NodeOrToken::into_token)
+                    .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
+                {
+                    let replace_with = t.clone_subtree().syntax().clone_for_update();
+                    ted::replace(self_tok, replace_with);
+                }
+            }
+        }
+    }
+
     let mut func_let_vars: BTreeSet<String> = BTreeSet::new();
 
     // grab all of the local variable declarations in the function
@@ -1510,4 +1520,106 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn inline_call_with_multiple_self_types_eq() {
+        check_assist(
+            inline_call,
+            r#"
+#[derive(PartialEq, Eq)]
+enum Enum {
+    A,
+    B,
+}
+
+impl Enum {
+    fn a_or_b_eq(&self) -> bool {
+        self == &Self::A || self == &Self::B
+    }
+}
+
+fn a() -> bool {
+    Enum::A.$0a_or_b_eq()
+}
+"#,
+            r#"
+#[derive(PartialEq, Eq)]
+enum Enum {
+    A,
+    B,
+}
+
+impl Enum {
+    fn a_or_b_eq(&self) -> bool {
+        self == &Self::A || self == &Self::B
+    }
+}
+
+fn a() -> bool {
+    {
+        let ref this = Enum::A;
+        this == &Enum::A || this == &Enum::B
+    }
+}
+"#,
+        )
+    }
+
+    #[test]
+    fn inline_call_with_self_type_in_macros() {
+        check_assist(
+            inline_call,
+            r#"
+trait Trait<T1> {
+    fn f(a: T1) -> Self;
+}
+
+macro_rules! impl_from {
+    ($t: ty) => {
+        impl Trait<$t> for $t {
+            fn f(a: $t) -> Self {
+                a as Self
+            }
+        }
+    };
+}
+
+struct A {}
+
+impl_from!(A);
+
+fn main() {
+    let a: A = A{};
+    let b = <A as Trait<A>>::$0f(a);
+}
+"#,
+            r#"
+trait Trait<T1> {
+    fn f(a: T1) -> Self;
+}
+
+macro_rules! impl_from {
+    ($t: ty) => {
+        impl Trait<$t> for $t {
+            fn f(a: $t) -> Self {
+                a as Self
+            }
+        }
+    };
+}
+
+struct A {}
+
+impl_from!(A);
+
+fn main() {
+    let a: A = A{};
+    let b = {
+        let a = a;
+      a as A
+    };
+}
+"#,
+        )
+    }
 }