diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index d5285c17106..d2081d22d79 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -8,8 +8,9 @@ use base_db::{CrateId, Edition}; use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; use hir_def::{ - item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId, - GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId, + data::ImplData, 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}; @@ -247,7 +248,7 @@ pub fn for_trait_and_self_ty( self.map .get(&trait_) .into_iter() - .flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty)))) + .flat_map(move |map| map.get(&Some(self_ty)).into_iter().chain(map.get(&None))) .flat_map(|v| v.iter().copied()) } @@ -575,6 +576,59 @@ pub(crate) fn iterate_method_candidates( slot } +pub fn lookup_impl_method( + self_ty: &Ty, + db: &dyn HirDatabase, + env: Arc, + trait_: TraitId, + name: &Name, +) -> Option { + 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, + table: &mut InferenceTable, + self_ty: &Ty, +) -> Option> { + 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, db: &dyn HirDatabase, @@ -970,18 +1024,31 @@ fn is_valid_candidate( self_ty: &Ty, visible_from_module: Option, ) -> 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); - if let Some(name) = name { - if &data.name != name { - return false; + + 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); } - } + v + })); + table.run_in_snapshot(|table| { let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build(); - let expected_self_ty = match m.lookup(db.upcast()).container { + let expect_self_ty = match m.lookup(db.upcast()).container { ItemContainerId::TraitId(_) => { subst.at(Interner, 0).assert_ty_ref(Interner).clone() } @@ -993,49 +1060,31 @@ fn is_valid_candidate( unreachable!() } }; - if !table.unify(&expected_self_ty, &self_ty) { - return false; - } + check_that!(table.unify(&expect_self_ty, self_ty)); if let Some(receiver_ty) = receiver_ty { - if !data.has_self_param() { - return false; - } + check_that!(data.has_self_param()); 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); - if !receiver_matches { - return false; - } + check_that!(table.unify(&receiver_ty, &expected_receiver)); } - 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); - 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) { + 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 { 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 aa10b0f878f..744f3865aaa 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -16,7 +16,6 @@ name::{known, AsName}, ExpansionInfo, MacroCallId, }; -use hir_ty::Interner; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; @@ -975,18 +974,11 @@ fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option { } fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { - self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id) + self.analyze(call.syntax())?.resolve_method_call(self.db, call) } fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { - 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) + self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } fn resolve_field(&self, field: &ast::FieldExpr) -> Option { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 2298a75d57f..984a464ed16 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -21,7 +21,8 @@ path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, type_ref::Mutability, - AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, Lookup, ModuleDefId, VariantId, + AsMacroCall, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId, + Lookup, ModuleDefId, VariantId, }; use hir_expand::{ builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name::AsName, HirFileId, InFile, @@ -31,8 +32,8 @@ record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions, UnsafeExpr, }, - Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt, - TyLoweringContext, + method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, + TyExt, TyKind, TyLoweringContext, }; use smallvec::SmallVec; use syntax::{ @@ -42,8 +43,8 @@ use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, - BuiltinType, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, ToolModule, - Trait, Type, TypeAlias, Variant, + BuiltinType, Callable, 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 @@ -232,13 +233,29 @@ pub(crate) fn pattern_adjustments( ) } + pub(crate) fn resolve_method_call_as_callable( + &self, + db: &dyn HirDatabase, + call: &ast::MethodCallExpr, + ) -> Option { + 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, Substitution)> { + ) -> Option { let expr_id = self.expr_id(db, &call.clone().into())?; - self.infer.as_ref()?.method_resolution(expr_id) + 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)) } pub(crate) fn resolve_field( @@ -336,6 +353,25 @@ pub(crate) fn resolve_path( 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)) = @@ -563,6 +599,30 @@ pub(crate) fn is_unsafe_macro_call( } false } + + fn resolve_impl_method( + &self, + db: &dyn HirDatabase, + func: FunctionId, + substs: &Substitution, + ) -> Option { + 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 ba0a1427181..61cb2f04792 100644 --- a/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -1,8 +1,5 @@ -use hir::{ItemInNs, ModuleDef}; -use ide_db::{ - assists::{AssistId, AssistKind}, - imports::import_assets::item_for_path_search, -}; +use hir::{db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef}; +use ide_db::assists::{AssistId, AssistKind}; use syntax::{ast, AstNode}; use crate::{ @@ -67,6 +64,26 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt Some(()) } +fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option { + 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 { + 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 411e5b25b73..779cdbc93c5 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -460,6 +460,21 @@ 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 bff8265e014..c2e7142f52f 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -24,7 +24,7 @@ use base_db::{AnchoredPathBuf, FileId, FileRange}; use either::Either; -use hir::{AsAssocItem, FieldSource, HasSource, InFile, ModuleSource, Semantics}; +use hir::{FieldSource, HasSource, InFile, ModuleSource, Semantics}; use stdx::never; use syntax::{ ast::{self, HasName}, @@ -37,6 +37,7 @@ search::FileReference, source_change::{FileSystemEdit, SourceChange}, syntax_helpers::node_ext::expr_as_name_ref, + traits::convert_to_def_in_trait, RootDatabase, }; @@ -248,7 +249,7 @@ fn rename_mod( fn rename_reference( sema: &Semantics, - mut def: Definition, + def: Definition, new_name: &str, ) -> Result { let ident_kind = IdentifierKind::classify(new_name)?; @@ -275,41 +276,7 @@ fn rename_reference( } } - 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 def = convert_to_def_in_trait(sema.db, 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 4a11fb73cd6..692fce06b0f 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -16,6 +16,7 @@ use crate::{ defs::{Definition, NameClass, NameRefClass}, + traits::convert_to_def_in_trait, RootDatabase, }; @@ -619,7 +620,9 @@ fn found_name_ref( }; sink(file_id, reference) } - Some(NameRefClass::Definition(def)) if def == self.def => { + Some(NameRefClass::Definition(def)) + if convert_to_def_in_trait(self.sema.db, 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 0fbfd869921..666499ed7a5 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::RootDatabase; -use hir::Semantics; +use crate::{defs::Definition, RootDatabase}; +use hir::{db::HirDatabase, AsAssocItem, Semantics}; use rustc_hash::FxHashSet; use syntax::{ast, AstNode}; @@ -69,6 +69,28 @@ 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 df73879aed7..e10789fd47a 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,7 +1,7 @@ -use std::convert::TryInto; +use std::{convert::TryInto, mem::discriminant}; use crate::{doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo, TryToNav}; -use hir::{AsAssocItem, Semantics}; +use hir::{AsAssocItem, AssocItem, 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_find_trait_item_definition(sema.db, &def) + try_filter_trait_item_definition(sema, &def) .unwrap_or_else(|| def_to_nav(sema.db, def)) }) .collect(), @@ -104,32 +104,37 @@ fn try_lookup_include_path( docs: None, }) } - -/// finds the trait definition of an impl'd item +/// finds the trait definition of an impl'd item, except function /// e.g. /// ```rust -/// trait A { fn a(); } +/// trait A { type a; } /// struct S; -/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait +/// impl A for S { type a = i32; } // <-- on this associate type, will get the location of a in the trait /// ``` -fn try_find_trait_item_definition( - db: &RootDatabase, +fn try_filter_trait_item_definition( + sema: &Semantics, def: &Definition, ) -> Option> { - let name = def.name(db)?; + let db = sema.db; let assoc = def.as_assoc_item(db)?; - - 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]) + 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]) + } + } } fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec { @@ -172,6 +177,23 @@ fn check_unresolved(ra_fixture: &str) { 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( @@ -1331,23 +1353,161 @@ fn main() { "#, ); } - - #[test] - fn goto_def_of_trait_impl_fn() { - check( - r#" + #[cfg(test)] + mod goto_impl_of_trait_fn { + use super::check; + #[test] + fn cursor_on_impl() { + 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); +impl G for Gen { + fn g(&self) { + } +} +impl G for Gen +where T : Bound +{ + fn g(&self){ + //^ + } +} +struct A; +impl Bound for A{} +fn f() { + let gen = Gen::(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); +impl G for Gen +where T : Bound +{ + fn g(&self){ + //^ + } +} +struct A; +impl Bound for A{} +fn f() { + let gen = Gen::(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(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 b91d54dd927..788cbe92723 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -510,7 +510,11 @@ 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(sema.db)).is_some() { + if func + .as_assoc_item(sema.db) + .and_then(|it| it.containing_trait_or_trait_impl(sema.db)) + .is_some() + { h |= HlMod::Trait; } diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 6d2788d337d..884224960f5 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::Bar + foo::Foo ``` ```rust