about summary refs log tree commit diff
path: root/src/tools/rust-analyzer
diff options
context:
space:
mode:
authorChayim Refael Friedman <chayimfr@gmail.com>2025-08-13 05:23:58 +0300
committerChayim Refael Friedman <chayimfr@gmail.com>2025-08-13 05:23:58 +0300
commit49ebe9f849202561ac02ece6e8683a6602bf1f38 (patch)
treedecf4655b11e8ca6febca08b0866fc96b34f2fce /src/tools/rust-analyzer
parent40b8d413672af5ff3668e825a56cde1f24149095 (diff)
downloadrust-49ebe9f849202561ac02ece6e8683a6602bf1f38.tar.gz
rust-49ebe9f849202561ac02ece6e8683a6602bf1f38.zip
Only import the item in "Unqualify method call" if needed
Diffstat (limited to 'src/tools/rust-analyzer')
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs118
2 files changed, 122 insertions, 1 deletions
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 05f06f97fdc..6be44532d03 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -2247,6 +2247,11 @@ impl<'db> SemanticsScope<'db> {
         }
     }
 
+    /// Checks if a trait is in scope, either because of an import or because we're in an impl of it.
+    pub fn can_use_trait_methods(&self, t: Trait) -> bool {
+        self.resolver.traits_in_scope(self.db).contains(&t.id)
+    }
+
     /// Resolve a path as-if it was written at the given scope. This is
     /// necessary a heuristic, as it doesn't take hygiene into account.
     pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option<PathResolution> {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs
index 1f89a3d5f17..a58b1da621c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs
@@ -1,3 +1,4 @@
+use hir::AsAssocItem;
 use syntax::{
     TextRange,
     ast::{self, AstNode, HasArgList, prec::ExprPrecedence},
@@ -43,6 +44,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
     let qualifier = path.qualifier()?;
     let method_name = path.segment()?.name_ref()?;
 
+    let scope = ctx.sema.scope(path.syntax())?;
     let res = ctx.sema.resolve_path(&path)?;
     let hir::PathResolution::Def(hir::ModuleDef::Function(fun)) = res else { return None };
     if !fun.has_self_param(ctx.sema.db) {
@@ -78,7 +80,14 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
                 edit.insert(close, ")");
             }
             edit.replace(replace_comma, format!(".{method_name}("));
-            add_import(qualifier, ctx, edit);
+
+            if let Some(fun) = fun.as_assoc_item(ctx.db())
+                && let Some(trait_) = fun.container_or_implemented_trait(ctx.db())
+                && !scope.can_use_trait_methods(trait_)
+            {
+                // Only add an import for trait methods that are not already imported.
+                add_import(qualifier, ctx, edit);
+            }
         },
     )
 }
@@ -235,4 +244,111 @@ impl S { fn assoc(S: S, S: S) {} }
 fn f() { S::assoc$0(S, S); }"#,
         );
     }
+
+    #[test]
+    fn inherent_method() {
+        check_assist(
+            unqualify_method_call,
+            r#"
+mod foo {
+    pub struct Bar;
+    impl Bar {
+        pub fn bar(self) {}
+    }
+}
+
+fn baz() {
+    foo::Bar::b$0ar(foo::Bar);
+}
+        "#,
+            r#"
+mod foo {
+    pub struct Bar;
+    impl Bar {
+        pub fn bar(self) {}
+    }
+}
+
+fn baz() {
+    foo::Bar.bar();
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn trait_method_in_impl() {
+        check_assist(
+            unqualify_method_call,
+            r#"
+mod foo {
+    pub trait Bar {
+        pub fn bar(self) {}
+    }
+}
+
+struct Baz;
+impl foo::Bar for Baz {
+    fn bar(self) {
+        foo::Bar::b$0ar(Baz);
+    }
+}
+        "#,
+            r#"
+mod foo {
+    pub trait Bar {
+        pub fn bar(self) {}
+    }
+}
+
+struct Baz;
+impl foo::Bar for Baz {
+    fn bar(self) {
+        Baz.bar();
+    }
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn trait_method_already_imported() {
+        check_assist(
+            unqualify_method_call,
+            r#"
+mod foo {
+    pub struct Foo;
+    pub trait Bar {
+        pub fn bar(self) {}
+    }
+    impl Bar for Foo {
+        pub fn bar(self) {}
+    }
+}
+
+use foo::Bar;
+
+fn baz() {
+    foo::Bar::b$0ar(foo::Foo);
+}
+        "#,
+            r#"
+mod foo {
+    pub struct Foo;
+    pub trait Bar {
+        pub fn bar(self) {}
+    }
+    impl Bar for Foo {
+        pub fn bar(self) {}
+    }
+}
+
+use foo::Bar;
+
+fn baz() {
+    foo::Foo.bar();
+}
+        "#,
+        );
+    }
 }