Repository: rust-lang/rust-analyzer
Pull request #12807 (2022-07-18 17:28:01)

Add simple support for completion item details
Supercedes https://github.com/rust-lang/rust-analyzer/pull/9891, this doesn't yet fix the linked issue since there is a lot of fancy things we can do there now.

This doesn't yet really implement anything new, it just adds the scaffolding for the protocol conversion


Patch:
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index d2081d22d7..d5285c1710 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -8,9 +8,8 @@ use arrayvec::ArrayVec;
 use base_db::{CrateId, Edition};
 use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
 use hir_def::{
-    data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
-    FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
-    TraitId,
+    item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId,
+    GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
 };
 use hir_expand::name::Name;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -248,7 +247,7 @@ impl TraitImpls {
         self.map
             .get(&trait_)
             .into_iter()
-            .flat_map(move |map| map.get(&Some(self_ty)).into_iter().chain(map.get(&None)))
+            .flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty))))
             .flat_map(|v| v.iter().copied())
     }
 
@@ -576,59 +575,6 @@ pub(crate) fn iterate_method_candidates<T>(
     slot
 }
 
-pub fn lookup_impl_method(
-    self_ty: &Ty,
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
-    trait_: TraitId,
-    name: &Name,
-) -> Option<FunctionId> {
-    let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?;
-    let trait_impls = TraitImpls::trait_impls_in_deps_query(db, env.krate);
-    let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp);
-    let mut table = InferenceTable::new(db, env.clone());
-    find_matching_impl(impls, &mut table, &self_ty).and_then(|data| {
-        data.items.iter().find_map(|it| match it {
-            AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
-            _ => None,
-        })
-    })
-}
-
-fn find_matching_impl(
-    mut impls: impl Iterator<Item = ImplId>,
-    table: &mut InferenceTable,
-    self_ty: &Ty,
-) -> Option<Arc<ImplData>> {
-    let db = table.db;
-    loop {
-        let impl_ = impls.next()?;
-        let r = table.run_in_snapshot(|table| {
-            let impl_data = db.impl_data(impl_);
-            let substs =
-                TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build();
-            let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
-
-            table
-                .unify(self_ty, &impl_ty)
-                .then(|| {
-                    let wh_goals =
-                        crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
-                            .into_iter()
-                            .map(|b| b.cast(Interner));
-
-                    let goal = crate::Goal::all(Interner, wh_goals);
-
-                    table.try_obligation(goal).map(|_| impl_data)
-                })
-                .flatten()
-        });
-        if r.is_some() {
-            break r;
-        }
-    }
-}
-
 pub fn iterate_path_candidates(
     ty: &Canonical<Ty>,
     db: &dyn HirDatabase,
@@ -1024,31 +970,18 @@ fn is_valid_candidate(
     self_ty: &Ty,
     visible_from_module: Option<ModuleId>,
 ) -> bool {
-    macro_rules! check_that {
-        ($cond:expr) => {
-            if !$cond {
-                return false;
-            }
-        };
-    }
-
     let db = table.db;
     match item {
         AssocItemId::FunctionId(m) => {
             let data = db.function_data(m);
-
-            check_that!(name.map_or(true, |n| n == &data.name));
-            check_that!(visible_from_module.map_or(true, |from_module| {
-                let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module);
-                if !v {
-                    cov_mark::hit!(autoderef_candidate_not_visible);
+            if let Some(name) = name {
+                if &data.name != name {
+                    return false;
                 }
-                v
-            }));
-
+            }
             table.run_in_snapshot(|table| {
                 let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
-                let expect_self_ty = match m.lookup(db.upcast()).container {
+                let expected_self_ty = match m.lookup(db.upcast()).container {
                     ItemContainerId::TraitId(_) => {
                         subst.at(Interner, 0).assert_ty_ref(Interner).clone()
                     }
@@ -1060,31 +993,49 @@ fn is_valid_candidate(
                         unreachable!()
                     }
                 };
-                check_that!(table.unify(&expect_self_ty, self_ty));
+                if !table.unify(&expected_self_ty, &self_ty) {
+                    return false;
+                }
                 if let Some(receiver_ty) = receiver_ty {
-                    check_that!(data.has_self_param());
+                    if !data.has_self_param() {
+                        return false;
+                    }
 
                     let sig = db.callable_item_signature(m.into());
                     let expected_receiver =
                         sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
+                    let receiver_matches = table.unify(&receiver_ty, &expected_receiver);
 
-                    check_that!(table.unify(&receiver_ty, &expected_receiver));
+                    if !receiver_matches {
+                        return false;
+                    }
                 }
+                if let Some(from_module) = visible_from_module {
+                    if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) {
+                        cov_mark::hit!(autoderef_candidate_not_visible);
+                        return false;
+                    }
+                }
+
                 true
             })
         }
         AssocItemId::ConstId(c) => {
             let data = db.const_data(c);
-            check_that!(receiver_ty.is_none());
-
-            check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
-            check_that!(visible_from_module.map_or(true, |from_module| {
-                let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module);
-                if !v {
+            if receiver_ty.is_some() {
+                return false;
+            }
+            if let Some(name) = name {
+                if data.name.as_ref() != Some(name) {
+                    return false;
+                }
+            }
+            if let Some(from_module) = visible_from_module {
+                if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
                     cov_mark::hit!(const_candidate_not_visible);
+                    return false;
                 }
-                v
-            }));
+            }
             if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
                 let self_ty_matches = table.run_in_snapshot(|table| {
                     let subst =
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 744f3865aa..aa10b0f878 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -16,6 +16,7 @@ use hir_expand::{
     name::{known, AsName},
     ExpansionInfo, MacroCallId,
 };
+use hir_ty::Interner;
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
@@ -974,11 +975,18 @@ impl<'db> SemanticsImpl<'db> {
     }
 
     fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
-        self.analyze(call.syntax())?.resolve_method_call(self.db, call)
+        self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id)
     }
 
     fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
-        self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
+        let source_analyzer = self.analyze(call.syntax())?;
+        let (func, subst) = source_analyzer.resolve_method_call(self.db, call)?;
+        let ty = self.db.value_ty(func.into()).substitute(Interner, &subst);
+        let resolver = source_analyzer.resolver;
+        let ty = Type::new_with_resolver(self.db, &resolver, ty);
+        let mut res = ty.as_callable(self.db)?;
+        res.is_bound_method = true;
+        Some(res)
     }
 
     fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 984a464ed1..2298a75d57 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -21,8 +21,7 @@ use hir_def::{
     path::{ModPath, Path, PathKind},
     resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
     type_ref::Mutability,
-    AsMacroCall, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId,
-    Lookup, ModuleDefId, VariantId,
+    AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, Lookup, ModuleDefId, VariantId,
 };
 use hir_expand::{
     builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name::AsName, HirFileId, InFile,
@@ -32,8 +31,8 @@ use hir_ty::{
         record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
         UnsafeExpr,
     },
-    method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution,
-    TyExt, TyKind, TyLoweringContext,
+    Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt,
+    TyLoweringContext,
 };
 use smallvec::SmallVec;
 use syntax::{
@@ -43,8 +42,8 @@ use syntax::{
 
 use crate::{
     db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
-    BuiltinType, Callable, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct,
-    ToolModule, Trait, Type, TypeAlias, Variant,
+    BuiltinType, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, ToolModule,
+    Trait, Type, TypeAlias, Variant,
 };
 
 /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -233,29 +232,13 @@ impl SourceAnalyzer {
         )
     }
 
-    pub(crate) fn resolve_method_call_as_callable(
-        &self,
-        db: &dyn HirDatabase,
-        call: &ast::MethodCallExpr,
-    ) -> Option<Callable> {
-        let expr_id = self.expr_id(db, &call.clone().into())?;
-        let (func, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
-        let ty = db.value_ty(func.into()).substitute(Interner, &substs);
-        let ty = Type::new_with_resolver(db, &self.resolver, ty);
-        let mut res = ty.as_callable(db)?;
-        res.is_bound_method = true;
-        Some(res)
-    }
-
     pub(crate) fn resolve_method_call(
         &self,
         db: &dyn HirDatabase,
         call: &ast::MethodCallExpr,
-    ) -> Option<FunctionId> {
+    ) -> Option<(FunctionId, Substitution)> {
         let expr_id = self.expr_id(db, &call.clone().into())?;
-        let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
-        let f_in_impl = self.resolve_impl_method(db, f_in_trait, &substs);
-        f_in_impl.or(Some(f_in_trait))
+        self.infer.as_ref()?.method_resolution(expr_id)
     }
 
     pub(crate) fn resolve_field(
@@ -353,25 +336,6 @@ impl SourceAnalyzer {
                 let expr_id = self.expr_id(db, &path_expr.into())?;
                 let infer = self.infer.as_ref()?;
                 if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) {
-                    let assoc = match assoc {
-                        AssocItemId::FunctionId(f_in_trait) => {
-                            match infer.type_of_expr.get(expr_id) {
-                                None => assoc,
-                                Some(func_ty) => {
-                                    if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
-                                        self.resolve_impl_method(db, f_in_trait, subs)
-                                            .map(AssocItemId::FunctionId)
-                                            .unwrap_or(assoc)
-                                    } else {
-                                        assoc
-                                    }
-                                }
-                            }
-                        }
-
-                        _ => assoc,
-                    };
-
                     return Some(PathResolution::Def(AssocItem::from(assoc).into()));
                 }
                 if let Some(VariantId::EnumVariantId(variant)) =
@@ -599,30 +563,6 @@ impl SourceAnalyzer {
         }
         false
     }
-
-    fn resolve_impl_method(
-        &self,
-        db: &dyn HirDatabase,
-        func: FunctionId,
-        substs: &Substitution,
-    ) -> Option<FunctionId> {
-        let impled_trait = match func.lookup(db.upcast()).container {
-            ItemContainerId::TraitId(trait_id) => trait_id,
-            _ => return None,
-        };
-        if substs.is_empty(Interner) {
-            return None;
-        }
-        let self_ty = substs.at(Interner, 0).ty(Interner)?;
-        let krate = self.resolver.krate();
-        let trait_env = self.resolver.body_owner()?.as_generic_def_id().map_or_else(
-            || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
-            |d| db.trait_environment(d),
-        );
-
-        let fun_data = db.function_data(func);
-        method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
-    }
 }
 
 fn scope_for(
diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs
index 61cb2f0479..ba0a142718 100644
--- a/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -1,5 +1,8 @@
-use hir::{db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef};
-use ide_db::assists::{AssistId, AssistKind};
+use hir::{ItemInNs, ModuleDef};
+use ide_db::{
+    assists::{AssistId, AssistKind},
+    imports::import_assets::item_for_path_search,
+};
 use syntax::{ast, AstNode};
 
 use crate::{
@@ -64,26 +67,6 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt
     Some(())
 }
 
-fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option<ItemInNs> {
-    Some(match item {
-        ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
-            Some(assoc_item) => match assoc_item.container(db) {
-                AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
-                AssocItemContainer::Impl(impl_) => match impl_.trait_(db) {
-                    None => ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)),
-                    Some(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
-                },
-            },
-            None => item,
-        },
-        ItemInNs::Macros(_) => item,
-    })
-}
-
-fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> {
-    item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db))
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs
index 779cdbc93c..411e5b25b7 100644
--- a/crates/ide-assists/src/utils/suggest_name.rs
+++ b/crates/ide-assists/src/utils/suggest_name.rs
@@ -460,21 +460,6 @@ fn foo() { S.bar($01$0, 2) }
         );
     }
 
-    #[test]
-    fn method_on_impl_trait() {
-        check(
-            r#"
-struct S;
-trait T {
-    fn bar(&self, n: i32, m: u32);
-}
-impl T for S { fn bar(&self, n: i32, m: u32); }
-fn foo() { S.bar($01$0, 2) }
-"#,
-            "n",
-        );
-    }
-
     #[test]
     fn method_ufcs() {
         check(
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index c2e7142f52..bff8265e01 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -24,7 +24,7 @@ use std::fmt;
 
 use base_db::{AnchoredPathBuf, FileId, FileRange};
 use either::Either;
-use hir::{FieldSource, HasSource, InFile, ModuleSource, Semantics};
+use hir::{AsAssocItem, FieldSource, HasSource, InFile, ModuleSource, Semantics};
 use stdx::never;
 use syntax::{
     ast::{self, HasName},
@@ -37,7 +37,6 @@ use crate::{
     search::FileReference,
     source_change::{FileSystemEdit, SourceChange},
     syntax_helpers::node_ext::expr_as_name_ref,
-    traits::convert_to_def_in_trait,
     RootDatabase,
 };
 
@@ -249,7 +248,7 @@ fn rename_mod(
 
 fn rename_reference(
     sema: &Semantics<RootDatabase>,
-    def: Definition,
+    mut def: Definition,
     new_name: &str,
 ) -> Result<SourceChange> {
     let ident_kind = IdentifierKind::classify(new_name)?;
@@ -276,7 +275,41 @@ fn rename_reference(
         }
     }
 
-    let def = convert_to_def_in_trait(sema.db, def);
+    let assoc_item = match def {
+        // HACK: resolve trait impl items to the item def of the trait definition
+        // so that we properly resolve all trait item references
+        Definition::Function(it) => it.as_assoc_item(sema.db),
+        Definition::TypeAlias(it) => it.as_assoc_item(sema.db),
+        Definition::Const(it) => it.as_assoc_item(sema.db),
+        _ => None,
+    };
+    def = match assoc_item {
+        Some(assoc) => assoc
+            .containing_trait_impl(sema.db)
+            .and_then(|trait_| {
+                trait_.items(sema.db).into_iter().find_map(|it| match (it, assoc) {
+                    (hir::AssocItem::Function(trait_func), hir::AssocItem::Function(func))
+                        if trait_func.name(sema.db) == func.name(sema.db) =>
+                    {
+                        Some(Definition::Function(trait_func))
+                    }
+                    (hir::AssocItem::Const(trait_konst), hir::AssocItem::Const(konst))
+                        if trait_konst.name(sema.db) == konst.name(sema.db) =>
+                    {
+                        Some(Definition::Const(trait_konst))
+                    }
+                    (
+                        hir::AssocItem::TypeAlias(trait_type_alias),
+                        hir::AssocItem::TypeAlias(type_alias),
+                    ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => {
+                        Some(Definition::TypeAlias(trait_type_alias))
+                    }
+                    _ => None,
+                })
+            })
+            .unwrap_or(def),
+        None => def,
+    };
     let usages = def.usages(sema).all();
 
     if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 692fce06b0..4a11fb73cd 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -16,7 +16,6 @@ use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
 
 use crate::{
     defs::{Definition, NameClass, NameRefClass},
-    traits::convert_to_def_in_trait,
     RootDatabase,
 };
 
@@ -620,9 +619,7 @@ impl<'a> FindUsages<'a> {
                 };
                 sink(file_id, reference)
             }
-            Some(NameRefClass::Definition(def))
-                if convert_to_def_in_trait(self.sema.db, def) == self.def =>
-            {
+            Some(NameRefClass::Definition(def)) if def == self.def => {
                 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
                 let reference = FileReference {
                     range,
diff --git a/crates/ide-db/src/traits.rs b/crates/ide-db/src/traits.rs
index 666499ed7a..0fbfd86992 100644
--- a/crates/ide-db/src/traits.rs
+++ b/crates/ide-db/src/traits.rs
@@ -1,7 +1,7 @@
 //! Functionality for obtaining data related to traits from the DB.
 
-use crate::{defs::Definition, RootDatabase};
-use hir::{db::HirDatabase, AsAssocItem, Semantics};
+use crate::RootDatabase;
+use hir::Semantics;
 use rustc_hash::FxHashSet;
 use syntax::{ast, AstNode};
 
@@ -69,28 +69,6 @@ pub fn get_missing_assoc_items(
     })
 }
 
-/// Converts associated trait impl items to their trait definition counterpart
-pub(crate) fn convert_to_def_in_trait(db: &dyn HirDatabase, def: Definition) -> Definition {
-    use hir::AssocItem::*;
-    (|| {
-        let assoc = def.as_assoc_item(db)?;
-        let trait_ = assoc.containing_trait_impl(db)?;
-        let name = match assoc {
-            Function(it) => it.name(db),
-            Const(it) => it.name(db)?,
-            TypeAlias(it) => it.name(db),
-        };
-        let item = trait_.items(db).into_iter().find(|it| match (it, assoc) {
-            (Function(trait_func), Function(_)) => trait_func.name(db) == name,
-            (Const(trait_konst), Const(_)) => trait_konst.name(db).map_or(false, |it| it == name),
-            (TypeAlias(trait_type_alias), TypeAlias(_)) => trait_type_alias.name(db) == name,
-            _ => false,
-        })?;
-        Some(Definition::from(item))
-    })()
-    .unwrap_or(def)
-}
-
 #[cfg(test)]
 mod tests {
     use base_db::{fixture::ChangeFixture, FilePosition};
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index e10789fd47..df73879aed 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,7 +1,7 @@
-use std::{convert::TryInto, mem::discriminant};
+use std::convert::TryInto;
 
 use crate::{doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo, TryToNav};
-use hir::{AsAssocItem, AssocItem, Semantics};
+use hir::{AsAssocItem, Semantics};
 use ide_db::{
     base_db::{AnchoredPath, FileId, FileLoader},
     defs::{Definition, IdentClass},
@@ -65,7 +65,7 @@ pub(crate) fn goto_definition(
                     .definitions()
                     .into_iter()
                     .flat_map(|def| {
-                        try_filter_trait_item_definition(sema, &def)
+                        try_find_trait_item_definition(sema.db, &def)
                             .unwrap_or_else(|| def_to_nav(sema.db, def))
                     })
                     .collect(),
@@ -104,37 +104,32 @@ fn try_lookup_include_path(
         docs: None,
     })
 }
-/// finds the trait definition of an impl'd item, except function
+
+/// finds the trait definition of an impl'd item
 /// e.g.
 /// ```rust
-/// trait A { type a; }
+/// trait A { fn a(); }
 /// struct S;
-/// impl A for S { type a = i32; } // <-- on this associate type, will get the location of a in the trait
+/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
 /// ```
-fn try_filter_trait_item_definition(
-    sema: &Semantics<RootDatabase>,
+fn try_find_trait_item_definition(
+    db: &RootDatabase,
     def: &Definition,
 ) -> Option<Vec<NavigationTarget>> {
-    let db = sema.db;
+    let name = def.name(db)?;
     let assoc = def.as_assoc_item(db)?;
-    match assoc {
-        AssocItem::Function(..) => None,
-        AssocItem::Const(..) | AssocItem::TypeAlias(..) => {
-            let imp = match assoc.container(db) {
-                hir::AssocItemContainer::Impl(imp) => imp,
-                _ => return None,
-            };
-            let trait_ = imp.trait_(db)?;
-            let name = def.name(db)?;
-            let discri_value = discriminant(&assoc);
-            trait_
-                .items(db)
-                .iter()
-                .filter(|itm| discriminant(*itm) == discri_value)
-                .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
-                .map(|it| vec![it])
-        }
-    }
+
+    let imp = match assoc.container(db) {
+        hir::AssocItemContainer::Impl(imp) => imp,
+        _ => return None,
+    };
+
+    let trait_ = imp.trait_(db)?;
+    trait_
+        .items(db)
+        .iter()
+        .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
+        .map(|it| vec![it])
 }
 
 fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec<NavigationTarget> {
@@ -177,23 +172,6 @@ mod tests {
         assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs)
     }
 
-    #[test]
-    fn goto_def_if_items_same_name() {
-        check(
-            r#"
-trait Trait {
-    type A;
-    const A: i32;
-        //^
-}
-
-struct T;
-impl Trait for T {
-    type A = i32;
-    const A$0: i32 = -9;
-}"#,
-        );
-    }
     #[test]
     fn goto_def_in_mac_call_in_attr_invoc() {
         check(
@@ -1353,161 +1331,23 @@ fn main() {
 "#,
         );
     }
-    #[cfg(test)]
-    mod goto_impl_of_trait_fn {
-        use super::check;
-        #[test]
-        fn cursor_on_impl() {
-            check(
-                r#"
+
+    #[test]
+    fn goto_def_of_trait_impl_fn() {
+        check(
+            r#"
 trait Twait {
     fn a();
+    // ^
 }
 
 struct Stwuct;
 
 impl Twait for Stwuct {
     fn a$0();
-     //^
-}
-        "#,
-            );
-        }
-        #[test]
-        fn method_call() {
-            check(
-                r#"
-trait Twait {
-    fn a(&self);
-}
-
-struct Stwuct;
-
-impl Twait for Stwuct {
-    fn a(&self){};
-     //^
-}
-fn f() {
-    let s = Stwuct;
-    s.a$0();
-}
-        "#,
-            );
-        }
-        #[test]
-        fn path_call() {
-            check(
-                r#"
-trait Twait {
-    fn a(&self);
-}
-
-struct Stwuct;
-
-impl Twait for Stwuct {
-    fn a(&self){};
-     //^
-}
-fn f() {
-    let s = Stwuct;
-    Stwuct::a$0(&s);
-}
-        "#,
-            );
-        }
-        #[test]
-        fn where_clause_can_work() {
-            check(
-                r#"
-trait G {
-    fn g(&self);
-}
-trait Bound{}
-trait EA{}
-struct Gen<T>(T);
-impl <T:EA> G for Gen<T> {
-    fn g(&self) {
-    }
-}
-impl <T> G for Gen<T>
-where T : Bound
-{
-    fn g(&self){
-     //^
-    }
-}
-struct A;
-impl Bound for A{}
-fn f() {
-    let gen = Gen::<A>(A);
-    gen.g$0();
-}
-                "#,
-            );
-        }
-        #[test]
-        fn wc_case_is_ok() {
-            check(
-                r#"
-trait G {
-    fn g(&self);
-}
-trait BParent{}
-trait Bound: BParent{}
-struct Gen<T>(T);
-impl <T> G for Gen<T>
-where T : Bound
-{
-    fn g(&self){
-     //^
-    }
-}
-struct A;
-impl Bound for A{}
-fn f() {
-    let gen = Gen::<A>(A);
-    gen.g$0();
 }
 "#,
-            );
-        }
-
-        #[test]
-        fn method_call_defaulted() {
-            check(
-                r#"
-trait Twait {
-    fn a(&self) {}
-     //^
-}
-
-struct Stwuct;
-
-impl Twait for Stwuct {
-}
-fn f() {
-    let s = Stwuct;
-    s.a$0();
-}
-        "#,
-            );
-        }
-
-        #[test]
-        fn method_call_on_generic() {
-            check(
-                r#"
-trait Twait {
-    fn a(&self) {}
-     //^
-}
-
-fn f<T: Twait>(s: T) {
-    s.a$0();
-}
-        "#,
-            );
-        }
+        );
     }
 
     #[test]
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 788cbe9272..b91d54dd92 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -510,11 +510,7 @@ fn highlight_method_call(
     if func.is_async(sema.db) {
         h |= HlMod::Async;
     }
-    if func
-        .as_assoc_item(sema.db)
-        .and_then(|it| it.containing_trait_or_trait_impl(sema.db))
-        .is_some()
-    {
+    if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() {
         h |= HlMod::Trait;
     }
 
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 7bff6a8b0e..cda95cd862 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,16 +1,16 @@
 //! Advertises the capabilities of the LSP Server.
 use lsp_types::{
     CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
-    CodeActionProviderCapability, CodeLensOptions, CompletionOptions, DeclarationCapability,
-    DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
-    FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
-    HoverProviderCapability, ImplementationProviderCapability, InlayHintOptions,
-    InlayHintServerCapabilities, OneOf, RenameOptions, SaveOptions,
-    SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
-    SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
-    TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
-    WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
-    WorkspaceServerCapabilities,
+    CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
+    CompletionOptionsCompletionItem, DeclarationCapability, DocumentOnTypeFormattingOptions,
+    FileOperationFilter, FileOperationPattern, FileOperationPatternKind,
+    FileOperationRegistrationOptions, FoldingRangeProviderCapability, HoverProviderCapability,
+    ImplementationProviderCapability, InlayHintOptions, InlayHintServerCapabilities, OneOf,
+    RenameOptions, SaveOptions, SelectionRangeProviderCapability, SemanticTokensFullOptions,
+    SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions,
+    TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
+    TypeDefinitionProviderCapability, WorkDoneProgressOptions,
+    WorkspaceFileOperationsServerCapabilities, WorkspaceServerCapabilities,
 };
 use serde_json::json;
 
@@ -36,7 +36,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
                 "(".to_string(),
             ]),
             all_commit_characters: None,
-            completion_item: None,
+            completion_item: completion_item(&config),
             work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
         }),
         signature_help_provider: Some(SignatureHelpOptions {
@@ -170,6 +170,12 @@ pub(crate) fn completion_item_edit_resolve(caps: &ClientCapabilities) -> bool {
     })() == Some(true)
 }
 
+fn completion_item(config: &Config) -> Option<CompletionOptionsCompletionItem> {
+    Some(CompletionOptionsCompletionItem {
+        label_details_support: Some(config.completion_label_details_support()),
+    })
+}
+
 fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
     client_caps
         .text_document
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index d498d8fe32..23305b2615 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -213,7 +213,7 @@ config_data! {
         /// also need to add the folders to Code's `files.watcherExclude`.
         files_excludeDirs: Vec<PathBuf> = "[]",
         /// Controls file watching implementation.
-        files_watcher: FilesWatcherDef = "\"client\"",
+        files_watcher: String = "\"client\"",
 
         /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
         highlightRelated_breakPoints_enable: bool = "true",
@@ -524,7 +524,7 @@ pub struct FilesConfig {
 #[derive(Debug, Clone)]
 pub enum FilesWatcher {
     Client,
-    Server,
+    Notify,
 }
 
 #[derive(Debug, Clone)]
@@ -833,6 +833,20 @@ impl Config {
         )
     }
 
+    pub fn completion_label_details_support(&self) -> bool {
+        try_!(self
+            .caps
+            .text_document
+            .as_ref()?
+            .completion
+            .as_ref()?
+            .completion_item
+            .as_ref()?
+            .label_details_support
+            .as_ref()?)
+        .is_some()
+    }
+
     pub fn offset_encoding(&self) -> OffsetEncoding {
         if supports_utf8(&self.caps) {
             OffsetEncoding::Utf8
@@ -903,11 +917,12 @@ impl Config {
 
     pub fn files(&self) -> FilesConfig {
         FilesConfig {
-            watcher: match self.data.files_watcher {
-                FilesWatcherDef::Client if self.did_change_watched_files_dynamic_registration() => {
+            watcher: match self.data.files_watcher.as_str() {
+                "notify" => FilesWatcher::Notify,
+                "client" if self.did_change_watched_files_dynamic_registration() => {
                     FilesWatcher::Client
                 }
-                _ => FilesWatcher::Server,
+                _ => FilesWatcher::Notify,
             },
             exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(),
         }
@@ -1422,7 +1437,7 @@ enum ManifestOrProjectJson {
 
 #[derive(Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
-enum ExprFillDefaultDef {
+pub enum ExprFillDefaultDef {
     Todo,
     Default,
 }
@@ -1485,14 +1500,6 @@ enum ReborrowHintsDef {
     Mutable,
 }
 
-#[derive(Deserialize, Debug, Clone)]
-#[serde(rename_all = "snake_case")]
-enum FilesWatcherDef {
-    Client,
-    Notify,
-    Server,
-}
-
 #[derive(Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum ImportPrefixDef {
@@ -1850,14 +1857,6 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 "Show only the parameters."
             ],
         },
-        "FilesWatcherDef" => set! {
-            "type": "string",
-            "enum": ["client", "server"],
-            "enumDescriptions": [
-                "Use the client (editor) to watch files for changes",
-                "Use server-side file watching",
-            ],
-        },
         _ => panic!("missing entry for {}: {}", ty, default),
     }
 
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 77125f88f4..13bcb7dfa2 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -320,7 +320,7 @@ impl GlobalState {
 
         let watch = match files_config.watcher {
             FilesWatcher::Client => vec![],
-            FilesWatcher::Server => project_folders.watch,
+            FilesWatcher::Notify => project_folders.watch,
         };
         self.vfs_config_version += 1;
         self.loader.handle.set_config(vfs::loader::Config {
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index cdd152ccf5..9c8e618b6e 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -277,6 +277,13 @@ fn completion_item(
         ..Default::default()
     };
 
+    if config.completion_label_details_support() {
+        lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
+            detail: None,
+            description: lsp_item.detail.clone(),
+        });
+    }
+
     set_score(&mut lsp_item, max_relevance, item.relevance());
 
     if config.completion().enable_imports_on_the_fly {
diff --git a/editors/code/package.json b/editors/code/package.json
index 020e3b0331..98ea922e4a 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -686,15 +686,7 @@
                 "rust-analyzer.files.watcher": {
                     "markdownDescription": "Controls file watching implementation.",
                     "default": "client",
-                    "type": "string",
-                    "enum": [
-                        "client",
-                        "server"
-                    ],
-                    "enumDescriptions": [
-                        "Use the client (editor) to watch files for changes",
-                        "Use server-side file watching"
-                    ]
+                    "type": "string"
                 },
                 "rust-analyzer.highlightRelated.breakPoints.enable": {
                     "markdownDescription": "Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.",


Test patch:
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 884224960f..6d2788d337 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -919,7 +919,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
     expect![[r#"
 
         ```rust
-        foo::Foo
+        foo::Bar
         ```
 
         ```rust