From e7b3a2fbd025750c4deb031c5e1d3702b25be686 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 16 May 2026 11:11:56 +0800 Subject: [PATCH 1/2] fix: not complete same name inherent deref methods Example --- ```rust fn test(a: A) { a.$0 } impl core::ops::Deref for A { type Target = B; fn deref(&self) -> &Self::Target { loop {} } } trait Foo { fn foo(&self) -> u32 {} } impl Foo for A {} impl Foo for B {} impl A { fn foo(&self) -> u8 {} } impl B { fn foo(&self) -> u16 {} } struct A {} struct B {} ``` **Before this PR** ```text me foo() fn(&self) -> u8 me foo() fn(&self) -> u16 me foo() (as Foo) fn(&self) -> u32 ``` **After this PR** ```text me foo() fn(&self) -> u8 me foo() (as Foo) fn(&self) -> u32 ``` --- crates/ide-completion/src/completions/dot.rs | 88 ++++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index c64bdf6bc5e1..cc6df718ce0a 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -1,9 +1,9 @@ //! Completes references after dot (fields and method calls). -use std::ops::ControlFlow; +use std::{collections::hash_map, ops::ControlFlow}; -use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback}; -use ide_db::FxHashSet; +use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use ide_db::{FxHashMap, FxHashSet}; use itertools::Either; use syntax::SmolStr; @@ -239,6 +239,9 @@ fn complete_methods( // duplicated, trait methods can. And it is still useful to show all of them (even when there // is also an inherent method, especially considering that it may be private, and filtered later). seen_methods: FxHashSet, + // However, duplicate inherent methods is usually meaningless + // https://github.com/rust-lang/rust-analyzer/issues/20773#issuecomment-4302781553 + seen_inherent_methods: FxHashMap, } impl MethodCandidateCallback for Callback<'_, '_, F> @@ -249,7 +252,21 @@ fn complete_methods( // `where` clauses or `dyn Trait`. fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { - (self.f)(func); + let same_name = self.seen_inherent_methods.entry(func.name(self.ctx.db)); + let do_complete = match &same_name { + hash_map::Entry::Vacant(_) => true, + hash_map::Entry::Occupied(same_func) => { + match self.ctx.is_visible(same_func.get()) { + crate::context::Visible::Yes => false, + crate::context::Visible::Editable => true, + crate::context::Visible::No => true, + } + } + }; + same_name.insert_entry(func); + if do_complete { + (self.f)(func); + } } ControlFlow::Continue(()) } @@ -277,7 +294,12 @@ fn complete_methods( &ctx.scope, traits_in_scope, None, - Callback { ctx, f, seen_methods: FxHashSet::default() }, + Callback { + ctx, + f, + seen_methods: FxHashSet::default(), + seen_inherent_methods: FxHashMap::default(), + }, ); } @@ -869,6 +891,62 @@ fn test(a: A) { ); } + #[test] + fn test_inherent_method_no_same_name() { + check_no_kw( + r#" +//- minicore: deref +struct A {} +struct B {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { fn foo(&self) -> u16 {} } +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &::Target + me foo() fn(&self) -> u8 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + + check_no_kw( + r#" +//- minicore: deref +//- /dep.rs crate:dep +pub struct A {} +pub struct B {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +pub trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { pub fn foo(&self) -> u16 {} } +//- /main.rs crate:main deps:dep +use dep::*; +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &::Target + me foo() fn(&self) -> u16 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + } + #[test] fn test_completion_works_in_consts() { check_no_kw( From be99012dfd0a14d7fb20e0d6940ef2906c82ce37 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 17 May 2026 12:38:39 +0800 Subject: [PATCH 2/2] Fix deref iterate order and multiple private in derefs Co-authored-by: Chayim Refael Friedman --- crates/hir-ty/src/method_resolution/probe.rs | 5 ++-- crates/ide-completion/src/completions/dot.rs | 26 +++++++++++++++++++- crates/ide-completion/src/tests/flyimport.rs | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs index 4b2f0cfd7066..60344490d550 100644 --- a/crates/hir-ty/src/method_resolution/probe.rs +++ b/crates/hir-ty/src/method_resolution/probe.rs @@ -3,6 +3,7 @@ use std::{cell::RefCell, convert::Infallible, ops::ControlFlow}; +use base_db::FxIndexMap; use hir_def::{ AssocItemId, FunctionId, GenericParamId, ImplId, ItemContainerId, TraitId, hir::generics::GenericParams, @@ -10,7 +11,7 @@ use hir_def::{ }; use hir_expand::name::Name; use rustc_ast_ir::Mutability; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use rustc_type_ir::{ InferTy, TypeVisitableExt, Upcast, Variance, elaborate::{self, supertrait_def_ids}, @@ -719,7 +720,7 @@ impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> { #[derive(Debug)] struct ProbeAllChoice<'db> { - candidates: RefCell>>, + candidates: RefCell>>, considering_visible_candidates: bool, } diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index cc6df718ce0a..59c6c55c22b9 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -263,8 +263,8 @@ fn complete_methods( } } }; - same_name.insert_entry(func); if do_complete { + same_name.insert_entry(func); (self.f)(func); } } @@ -924,15 +924,39 @@ fn test(a: A) { //- /dep.rs crate:dep pub struct A {} pub struct B {} +pub struct C {} +pub struct D {} +pub struct E {} +pub struct F {} impl core::ops::Deref for A { type Target = B; fn deref(&self) -> &Self::Target { loop {} } } +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for C { + type Target = D; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for D { + type Target = E; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for E { + type Target = F; + fn deref(&self) -> &Self::Target { loop {} } +} pub trait Foo { fn foo(&self) -> u32 {} } impl Foo for A {} impl Foo for B {} impl A { fn foo(&self) -> u8 {} } impl B { pub fn foo(&self) -> u16 {} } +impl C { fn foo(&self) -> i8 {} } +impl D { fn foo(&self) -> i16 {} } +impl E { pub fn foo(&self) -> i32 {} } +impl F { pub fn foo(&self) -> f32 {} } //- /main.rs crate:main deps:dep use dep::*; fn test(a: A) { diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 231623a42fc6..45db8ecfc6a5 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -781,9 +781,9 @@ fn main() { } "#, expect![[r#" - me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED + me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED "#]], ); }