From 1ef5e14c2c072fca9792d3da7c6443850790d779 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Wed, 15 Jun 2022 23:13:15 +0800 Subject: [PATCH 01/16] goto where trait method impl --- crates/hir-ty/src/method_resolution.rs | 231 ++++++++++++++++--------- crates/hir/src/semantics.rs | 7 + crates/hir/src/source_analyzer.rs | 61 ++++++- crates/ide-db/src/defs.rs | 45 +++++ crates/ide/src/goto_definition.rs | 139 +++++++++++---- 5 files changed, 366 insertions(+), 117 deletions(-) diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index d5285c1710..afac92b266 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -8,8 +8,9 @@ use arrayvec::ArrayVec; 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 @@ impl TraitImpls { 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,32 @@ pub(crate) fn iterate_method_candidates( slot } +pub fn lookup_trait_m_for_self_ty( + self_ty: &Ty, + db: &dyn HirDatabase, + env: Arc, + implied_trait: TraitId, + name: &Name, +) -> Option { + let self_ty_tp = 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(implied_trait, self_ty_tp); + let mut table = InferenceTable::new(db, env.clone()); + if let Some(data) = Valid::valid_impl(impls, &mut table, &self_ty) { + for &impl_item in data.items.iter() { + if Valid::is_valid_item(&mut table, Some(name), None, impl_item, self_ty, None) { + match impl_item { + AssocItemId::FunctionId(f) => { + return Some(f); + } + _ => (), + } + } + } + } + None +} + pub fn iterate_path_candidates( ty: &Canonical, db: &dyn HirDatabase, @@ -850,7 +877,7 @@ fn iterate_trait_method_candidates( for &(_, item) in data.items.iter() { // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. - if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { + if !Valid::is_valid_item(table, name, receiver_ty, item, self_ty, None) { continue; } if !known_implemented { @@ -932,8 +959,14 @@ fn iterate_inherent_methods( let impls_for_self_ty = impls.for_self_ty(self_ty); for &impl_def in impls_for_self_ty { for &item in &db.impl_data(impl_def).items { - if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module) - { + if !Valid::is_valid_item( + table, + name, + receiver_ty, + item, + self_ty, + visible_from_module, + ) { continue; } callback(receiver_adjustments.clone().unwrap_or_default(), item)?; @@ -961,97 +994,125 @@ pub fn resolve_indexing_op( } None } +struct Valid; +impl Valid { + fn valid_impl( + impls: impl Iterator, + table: &mut InferenceTable, + self_ty: &Ty, + ) -> Option> { + let db = table.db; + for impl_ in impls { + let impl_data = db.impl_data(impl_); + let substs = + TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build(); + let impl_ty = + substs.apply(db.impl_self_ty(impl_).into_value_and_skipped_binders().0, Interner); -fn is_valid_candidate( - table: &mut InferenceTable, - name: Option<&Name>, - receiver_ty: Option<&Ty>, - item: AssocItemId, - self_ty: &Ty, - visible_from_module: Option, -) -> bool { - 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; - } + if !table.unify(self_ty, &impl_ty) { + continue; } - 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 { - ItemContainerId::TraitId(_) => { - subst.at(Interner, 0).assert_ty_ref(Interner).clone() - } - ItemContainerId::ImplId(impl_id) => { - subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner) - } - // We should only get called for associated items (impl/trait) - ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { - unreachable!() - } - }; - if !table.unify(&expected_self_ty, &self_ty) { + + let wh_goals = crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs) + .into_iter() + .map(|b| b.into_well_formed_goal(Interner).cast(Interner)); + + let goal = crate::Goal::all(Interner, wh_goals); + + if table.try_obligation(goal).is_some() { + return Some(impl_data); + } + } + None + } + + fn is_valid_item( + table: &mut InferenceTable, + name: Option<&Name>, + receiver_ty: Option<&Ty>, + item: AssocItemId, + self_ty: &Ty, + visible_from_module: Option, + ) -> bool { + macro_rules! assert { + ($cond:expr) => { + if !$cond { return false; } - if let Some(receiver_ty) = receiver_ty { - 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); + let db = table.db; + match item { + AssocItemId::FunctionId(m) => { + let data = db.function_data(m); - 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) { + assert!(name.map_or(true, |n| n == &data.name)); + assert!(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 expect_self_ty = match m.lookup(db.upcast()).container { + ItemContainerId::TraitId(_) => { + subst.at(Interner, 0).assert_ty_ref(Interner).clone() + } + ItemContainerId::ImplId(impl_id) => { + subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner) + } + // We should only get called for associated items (impl/trait) + ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { + unreachable!() + } + }; + assert!(table.unify(&expect_self_ty, self_ty)); + if let Some(receiver_ty) = receiver_ty { + assert!(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); + + assert!(table.unify(&receiver_ty, &expected_receiver)); + } + true + }) + } + AssocItemId::ConstId(c) => { + let data = db.const_data(c); + assert!(receiver_ty.is_none()); + + assert!(name.map_or(true, |n| data.name.as_ref() == Some(n))); + assert!(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); + } + v + })); + if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container { + let self_ty_matches = table.run_in_snapshot(|table| { + let subst = + TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build(); + let expected_self_ty = + subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner); + table.unify(&expected_self_ty, &self_ty) + }); + if !self_ty_matches { + cov_mark::hit!(const_candidate_self_type_mismatch); return false; } } - true - }) + } + _ => false, } - 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) { - cov_mark::hit!(const_candidate_not_visible); - return false; - } - } - if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container { - let self_ty_matches = table.run_in_snapshot(|table| { - let subst = - TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build(); - let expected_self_ty = - subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner); - table.unify(&expected_self_ty, &self_ty) - }); - if !self_ty_matches { - cov_mark::hit!(const_candidate_self_type_mismatch); - return false; - } - } - true - } - _ => false, } } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index aa10b0f878..2574adb35a 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -348,6 +348,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_method_call(call).map(Function::from) } + pub fn resolve_impl_method(&self, call: &ast::Expr) -> Option { + self.imp.resolve_impl_method(call).map(Function::from) + } pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.imp.resolve_method_call_as_callable(call) } @@ -978,6 +981,10 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id) } + fn resolve_impl_method(&self, call: &ast::Expr) -> Option { + self.analyze(call.syntax())?.resolve_impl_method(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)?; diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index d624d37577..4d8d6a1460 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -21,7 +21,8 @@ use hir_def::{ path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, type_ref::Mutability, - AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, Lookup, ModuleDefId, VariantId, + AsMacroCall, 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 @@ use hir_ty::{ 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::{ @@ -247,6 +248,60 @@ impl SourceAnalyzer { self.infer.as_ref()?.method_resolution(expr_id) } + pub(crate) fn resolve_impl_method( + &self, + db: &dyn HirDatabase, + call: &ast::Expr, + ) -> Option { + let infered = self.infer.as_ref()?; + let expr_id = self.expr_id(db, call)?; + + let mut fun_info = None; + match call { + &ast::Expr::MethodCallExpr(..) => { + let (func, subs) = infered.method_resolution(expr_id)?; + if subs.is_empty(Interner) { + return None; + } + fun_info.replace((func, subs.at(Interner, 0).ty(Interner)?.clone())); + } + &ast::Expr::PathExpr(..) => { + let func_ty = infered.type_of_expr.get(expr_id)?; + if let TyKind::FnDef(fn_def, subs) = func_ty.kind(Interner) { + if subs.is_empty(Interner) { + return None; + } + if let hir_ty::CallableDefId::FunctionId(f_id) = + db.lookup_intern_callable_def(fn_def.clone().into()) + { + fun_info.replace((f_id, subs.at(Interner, 0).ty(Interner)?.clone())); + } + } + } + _ => (), + }; + let (func, self_ty) = fun_info?; + let implied_trait = match func.lookup(db.upcast()).container { + ItemContainerId::TraitId(trait_id) => trait_id, + _ => return None, + }; + + 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_trait_m_for_self_ty( + &self_ty, + db, + trait_env, + implied_trait, + &fun_data.name, + ) + } + pub(crate) fn resolve_field( &self, db: &dyn HirDatabase, diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 540cc70dd5..e5390eeb32 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -162,6 +162,22 @@ impl IdentClass { .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass)) } + pub fn classify_token_to_impl( + sema: &Semantics, + token: &SyntaxToken, + ) -> Option { + let p = token.parent()?; + match_ast! { + match p { + ast::NameRef(name_ref) => match NameRefClass::classify_to_impl(sema, name_ref)? { + NameRefClass::Definition(d) => Some(d), + _ => None, + }, + _ => None, + } + } + } + pub fn definitions(self) -> ArrayVec { let mut res = ArrayVec::new(); match self { @@ -417,6 +433,35 @@ impl NameRefClass { } } + fn classify_to_impl( + sema: &Semantics, + name_ref: ast::NameRef, + ) -> Option { + let parent = name_ref.syntax().parent()?; + match_ast! { + match parent { + ast::MethodCallExpr(method_call) => { + sema.resolve_impl_method(&ast::Expr::MethodCallExpr(method_call)) + .map(Definition::Function) + .map(NameRefClass::Definition) + }, + ast::PathSegment(ps) => { + ps.syntax().parent().and_then(ast::Path::cast) + .map(|p| + p.syntax() + .parent() + .and_then(ast::PathExpr::cast) + .map(|pe| + sema.resolve_impl_method(&ast::Expr::PathExpr(pe)) + .map(Definition::Function) + .map(NameRefClass::Definition) + ).flatten() + ).flatten() + }, + _=> None + } + } + } pub fn classify_lifetime( sema: &Semantics, lifetime: &ast::Lifetime, diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index df73879aed..ea7fd3e072 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; 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, &token) .unwrap_or_else(|| def_to_nav(sema.db, def)) }) .collect(), @@ -104,32 +104,38 @@ 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, + token: &SyntaxToken, ) -> 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(..) => { + IdentClass::classify_token_to_impl(sema, &token).map(|def| def_to_nav(db, def)) + } + 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)?; + 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 { @@ -1331,23 +1337,98 @@ 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] From 1064c7513a9cbe8a9d891a8ab20567b6649744d7 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Tue, 21 Jun 2022 20:05:49 +0800 Subject: [PATCH 02/16] reformat code --- crates/hir-ty/src/method_resolution.rs | 41 +++++++++++++++----------- crates/ide-db/src/defs.rs | 40 +++++++++++-------------- 2 files changed, 40 insertions(+), 41 deletions(-) diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index afac92b266..b61febe3a6 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -997,33 +997,38 @@ pub fn resolve_indexing_op( struct Valid; impl Valid { fn valid_impl( - impls: impl Iterator, + mut impls: impl Iterator, table: &mut InferenceTable, self_ty: &Ty, ) -> Option> { let db = table.db; - for impl_ in impls { - let impl_data = db.impl_data(impl_); - let substs = - TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build(); - let impl_ty = - substs.apply(db.impl_self_ty(impl_).into_value_and_skipped_binders().0, Interner); + 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 = substs + .apply(db.impl_self_ty(impl_).into_value_and_skipped_binders().0, Interner); - if !table.unify(self_ty, &impl_ty) { - continue; - } + 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.into_well_formed_goal(Interner).cast(Interner)); - let wh_goals = crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs) - .into_iter() - .map(|b| b.into_well_formed_goal(Interner).cast(Interner)); + let goal = crate::Goal::all(Interner, wh_goals); - let goal = crate::Goal::all(Interner, wh_goals); - - if table.try_obligation(goal).is_some() { - return Some(impl_data); + table.try_obligation(goal).map(|_| impl_data) + }) + .flatten() + }); + if r.is_some() { + break r; } } - None } fn is_valid_item( diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index e5390eeb32..2b1e8e325e 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -438,29 +438,23 @@ impl NameRefClass { name_ref: ast::NameRef, ) -> Option { let parent = name_ref.syntax().parent()?; - match_ast! { - match parent { - ast::MethodCallExpr(method_call) => { - sema.resolve_impl_method(&ast::Expr::MethodCallExpr(method_call)) - .map(Definition::Function) - .map(NameRefClass::Definition) - }, - ast::PathSegment(ps) => { - ps.syntax().parent().and_then(ast::Path::cast) - .map(|p| - p.syntax() - .parent() - .and_then(ast::PathExpr::cast) - .map(|pe| - sema.resolve_impl_method(&ast::Expr::PathExpr(pe)) - .map(Definition::Function) - .map(NameRefClass::Definition) - ).flatten() - ).flatten() - }, - _=> None - } - } + let expr = match_ast! { + match parent { + ast::MethodCallExpr(method_call) => { + Some(ast::Expr::MethodCallExpr(method_call)) + }, + ast::PathSegment(..) => { + parent.ancestors() + .find_map(ast::PathExpr::cast) + .map(ast::Expr::PathExpr) + }, + _=> None + } + }; + expr.as_ref() + .and_then(|e| sema.resolve_impl_method(e)) + .map(Definition::Function) + .map(NameRefClass::Definition) } pub fn classify_lifetime( sema: &Semantics, From 29f01cd9d2bcef560e165a2d4ff55b2546116e38 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 23 Jun 2022 14:10:12 +0200 Subject: [PATCH 03/16] Various cleanups - remove Valid, it serves no purpose and just obscures the diff - rename some things - don't use is_valid_candidate when searching for impl, it's not necessary --- crates/hir-ty/src/method_resolution.rs | 255 ++++++++++++------------- crates/hir/src/source_analyzer.rs | 10 +- 2 files changed, 121 insertions(+), 144 deletions(-) diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index b61febe3a6..a15514283b 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -576,30 +576,57 @@ pub(crate) fn iterate_method_candidates( slot } -pub fn lookup_trait_m_for_self_ty( +pub fn lookup_impl_method( self_ty: &Ty, db: &dyn HirDatabase, env: Arc, - implied_trait: TraitId, + trait_: TraitId, name: &Name, ) -> Option { - let self_ty_tp = TyFingerprint::for_trait_impl(self_ty)?; + 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(implied_trait, self_ty_tp); + let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp); let mut table = InferenceTable::new(db, env.clone()); - if let Some(data) = Valid::valid_impl(impls, &mut table, &self_ty) { - for &impl_item in data.items.iter() { - if Valid::is_valid_item(&mut table, Some(name), None, impl_item, self_ty, None) { - match impl_item { - AssocItemId::FunctionId(f) => { - return Some(f); - } - _ => (), - } - } + 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_some(*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.into_well_formed_goal(Interner).cast(Interner)); + + let goal = crate::Goal::all(Interner, wh_goals); + + table.try_obligation(goal).map(|_| impl_data) + }) + .flatten() + }); + if r.is_some() { + break r; } } - None } pub fn iterate_path_candidates( @@ -877,7 +904,7 @@ fn iterate_trait_method_candidates( for &(_, item) in data.items.iter() { // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. - if !Valid::is_valid_item(table, name, receiver_ty, item, self_ty, None) { + if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { continue; } if !known_implemented { @@ -959,14 +986,8 @@ fn iterate_inherent_methods( let impls_for_self_ty = impls.for_self_ty(self_ty); for &impl_def in impls_for_self_ty { for &item in &db.impl_data(impl_def).items { - if !Valid::is_valid_item( - table, - name, - receiver_ty, - item, - self_ty, - visible_from_module, - ) { + if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module) + { continue; } callback(receiver_adjustments.clone().unwrap_or_default(), item)?; @@ -994,130 +1015,92 @@ pub fn resolve_indexing_op( } None } -struct Valid; -impl Valid { - fn valid_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 = substs - .apply(db.impl_self_ty(impl_).into_value_and_skipped_binders().0, Interner); - 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.into_well_formed_goal(Interner).cast(Interner)); - - let goal = crate::Goal::all(Interner, wh_goals); - - table.try_obligation(goal).map(|_| impl_data) - }) - .flatten() - }); - if r.is_some() { - break r; +fn is_valid_candidate( + table: &mut InferenceTable, + name: Option<&Name>, + receiver_ty: Option<&Ty>, + item: AssocItemId, + self_ty: &Ty, + visible_from_module: Option, +) -> bool { + macro_rules! check_that { + ($cond:expr) => { + if !$cond { + return false; } - } + }; } - fn is_valid_item( - table: &mut InferenceTable, - name: Option<&Name>, - receiver_ty: Option<&Ty>, - item: AssocItemId, - self_ty: &Ty, - visible_from_module: Option, - ) -> bool { - macro_rules! assert { - ($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); } - }; - } + v + })); - let db = table.db; - match item { - AssocItemId::FunctionId(m) => { - let data = db.function_data(m); - - assert!(name.map_or(true, |n| n == &data.name)); - assert!(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); + 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 { + ItemContainerId::TraitId(_) => { + subst.at(Interner, 0).assert_ty_ref(Interner).clone() } - 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 { - ItemContainerId::TraitId(_) => { - subst.at(Interner, 0).assert_ty_ref(Interner).clone() - } - ItemContainerId::ImplId(impl_id) => { - subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner) - } - // We should only get called for associated items (impl/trait) - ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { - unreachable!() - } - }; - assert!(table.unify(&expect_self_ty, self_ty)); - if let Some(receiver_ty) = receiver_ty { - assert!(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); - - assert!(table.unify(&receiver_ty, &expected_receiver)); + ItemContainerId::ImplId(impl_id) => { + subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner) } - true - }) - } - AssocItemId::ConstId(c) => { - let data = db.const_data(c); - assert!(receiver_ty.is_none()); + // We should only get called for associated items (impl/trait) + ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { + unreachable!() + } + }; + check_that!(table.unify(&expect_self_ty, self_ty)); + if let Some(receiver_ty) = receiver_ty { + check_that!(data.has_self_param()); - assert!(name.map_or(true, |n| data.name.as_ref() == Some(n))); - assert!(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); - } - v - })); - if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container { - let self_ty_matches = table.run_in_snapshot(|table| { - let subst = - TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build(); - let expected_self_ty = - subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner); - table.unify(&expected_self_ty, &self_ty) - }); - if !self_ty_matches { - cov_mark::hit!(const_candidate_self_type_mismatch); - return false; - } + let sig = db.callable_item_signature(m.into()); + let expected_receiver = + sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst); + + check_that!(table.unify(&receiver_ty, &expected_receiver)); } true - } - _ => false, + }) } + 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 { + cov_mark::hit!(const_candidate_not_visible); + } + v + })); + if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container { + let self_ty_matches = table.run_in_snapshot(|table| { + let subst = + TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build(); + let expected_self_ty = + subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner); + table.unify(&expected_self_ty, &self_ty) + }); + if !self_ty_matches { + cov_mark::hit!(const_candidate_self_type_mismatch); + return false; + } + } + true + } + _ => false, } } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 4d8d6a1460..62bcc9de39 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -281,7 +281,7 @@ impl SourceAnalyzer { _ => (), }; let (func, self_ty) = fun_info?; - let implied_trait = match func.lookup(db.upcast()).container { + let impled_trait = match func.lookup(db.upcast()).container { ItemContainerId::TraitId(trait_id) => trait_id, _ => return None, }; @@ -293,13 +293,7 @@ impl SourceAnalyzer { ); let fun_data = db.function_data(func); - method_resolution::lookup_trait_m_for_self_ty( - &self_ty, - db, - trait_env, - implied_trait, - &fun_data.name, - ) + method_resolution::lookup_impl_method(&self_ty, db, trait_env, impled_trait, &fun_data.name) } pub(crate) fn resolve_field( From f410fdf6e308ae51191b0b816db76b6397260f55 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 23 Jun 2022 14:49:05 +0200 Subject: [PATCH 04/16] Add two more tests --- crates/ide/src/goto_definition.rs | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index ea7fd3e072..c3afe14fa3 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1429,6 +1429,43 @@ fn f() { "#, ); } + + #[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] From 36fadc4224622d8bfd220d37d4c19931f6cfc6c6 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Thu, 23 Jun 2022 16:05:59 +0200 Subject: [PATCH 05/16] Fix unstable feature use --- crates/hir-ty/src/method_resolution.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index a15514283b..76c76ee6cf 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -589,7 +589,7 @@ pub fn lookup_impl_method( 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_some(*f), + AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f), _ => None, }) }) From 6ecabe352a34a0fb290b2639e2eccd4317c85556 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Fri, 24 Jun 2022 19:11:35 +0800 Subject: [PATCH 06/16] functions resolve to impl --- crates/hir/src/semantics.rs | 7 --- crates/hir/src/source_analyzer.rs | 99 +++++++++++++++---------------- crates/ide-db/src/defs.rs | 39 ------------ crates/ide/src/goto_definition.rs | 7 +-- 4 files changed, 50 insertions(+), 102 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 2574adb35a..aa10b0f878 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -348,9 +348,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_method_call(call).map(Function::from) } - pub fn resolve_impl_method(&self, call: &ast::Expr) -> Option { - self.imp.resolve_impl_method(call).map(Function::from) - } pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.imp.resolve_method_call_as_callable(call) } @@ -981,10 +978,6 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id) } - fn resolve_impl_method(&self, call: &ast::Expr) -> Option { - self.analyze(call.syntax())?.resolve_impl_method(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)?; diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 62bcc9de39..3faae8fc4b 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -21,8 +21,8 @@ use hir_def::{ path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, type_ref::Mutability, - AsMacroCall, DefWithBodyId, FieldId, FunctionId, ItemContainerId, 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, @@ -245,55 +245,9 @@ impl SourceAnalyzer { call: &ast::MethodCallExpr, ) -> Option<(FunctionId, Substitution)> { let expr_id = self.expr_id(db, &call.clone().into())?; - self.infer.as_ref()?.method_resolution(expr_id) - } - - pub(crate) fn resolve_impl_method( - &self, - db: &dyn HirDatabase, - call: &ast::Expr, - ) -> Option { - let infered = self.infer.as_ref()?; - let expr_id = self.expr_id(db, call)?; - - let mut fun_info = None; - match call { - &ast::Expr::MethodCallExpr(..) => { - let (func, subs) = infered.method_resolution(expr_id)?; - if subs.is_empty(Interner) { - return None; - } - fun_info.replace((func, subs.at(Interner, 0).ty(Interner)?.clone())); - } - &ast::Expr::PathExpr(..) => { - let func_ty = infered.type_of_expr.get(expr_id)?; - if let TyKind::FnDef(fn_def, subs) = func_ty.kind(Interner) { - if subs.is_empty(Interner) { - return None; - } - if let hir_ty::CallableDefId::FunctionId(f_id) = - db.lookup_intern_callable_def(fn_def.clone().into()) - { - fun_info.replace((f_id, subs.at(Interner, 0).ty(Interner)?.clone())); - } - } - } - _ => (), - }; - let (func, self_ty) = fun_info?; - let impled_trait = match func.lookup(db.upcast()).container { - ItemContainerId::TraitId(trait_id) => trait_id, - _ => return None, - }; - - 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) + 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); + Some((f_in_impl.unwrap_or(f_in_trait), substs)) } pub(crate) fn resolve_field( @@ -391,6 +345,25 @@ 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)) = @@ -616,6 +589,30 @@ impl SourceAnalyzer { } 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-db/src/defs.rs b/crates/ide-db/src/defs.rs index 2b1e8e325e..540cc70dd5 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -162,22 +162,6 @@ impl IdentClass { .or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass)) } - pub fn classify_token_to_impl( - sema: &Semantics, - token: &SyntaxToken, - ) -> Option { - let p = token.parent()?; - match_ast! { - match p { - ast::NameRef(name_ref) => match NameRefClass::classify_to_impl(sema, name_ref)? { - NameRefClass::Definition(d) => Some(d), - _ => None, - }, - _ => None, - } - } - } - pub fn definitions(self) -> ArrayVec { let mut res = ArrayVec::new(); match self { @@ -433,29 +417,6 @@ impl NameRefClass { } } - fn classify_to_impl( - sema: &Semantics, - name_ref: ast::NameRef, - ) -> Option { - let parent = name_ref.syntax().parent()?; - let expr = match_ast! { - match parent { - ast::MethodCallExpr(method_call) => { - Some(ast::Expr::MethodCallExpr(method_call)) - }, - ast::PathSegment(..) => { - parent.ancestors() - .find_map(ast::PathExpr::cast) - .map(ast::Expr::PathExpr) - }, - _=> None - } - }; - expr.as_ref() - .and_then(|e| sema.resolve_impl_method(e)) - .map(Definition::Function) - .map(NameRefClass::Definition) - } pub fn classify_lifetime( sema: &Semantics, lifetime: &ast::Lifetime, diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index c3afe14fa3..e44b5020c3 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -65,7 +65,7 @@ pub(crate) fn goto_definition( .definitions() .into_iter() .flat_map(|def| { - try_filter_trait_item_definition(sema, &def, &token) + try_filter_trait_item_definition(sema, &def) .unwrap_or_else(|| def_to_nav(sema.db, def)) }) .collect(), @@ -114,14 +114,11 @@ fn try_lookup_include_path( fn try_filter_trait_item_definition( sema: &Semantics, def: &Definition, - token: &SyntaxToken, ) -> Option> { let db = sema.db; let assoc = def.as_assoc_item(db)?; match assoc { - AssocItem::Function(..) => { - IdentClass::classify_token_to_impl(sema, &token).map(|def| def_to_nav(db, def)) - } + AssocItem::Function(..) => None, AssocItem::Const(..) | AssocItem::TypeAlias(..) => { let imp = match assoc.container(db) { hir::AssocItemContainer::Impl(imp) => imp, From 9e6bff79f460701f82e01e614b890d86edbb91c7 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Fri, 24 Jun 2022 19:15:16 +0800 Subject: [PATCH 07/16] fix some test due to resolve to where trait m impl --- .../src/handlers/qualify_method_call.rs | 10 ++-- crates/ide-db/src/rename.rs | 60 ++++++++++--------- crates/ide-db/src/search.rs | 4 +- .../test_data/highlight_unsafe.html | 2 +- 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs index ba0a142718..7d642e45e8 100644 --- a/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -281,7 +281,7 @@ use test_mod::*; fn main() { let test_struct = test_mod::TestStruct {}; - TestTrait::test_method(&test_struct) + TestStruct::test_method(&test_struct) } "#, ); @@ -324,7 +324,7 @@ use test_mod::*; fn main() { let test_struct = test_mod::TestStruct {}; - TestTrait::test_method(&test_struct, 12, 32u) + TestStruct::test_method(&test_struct, 12, 32u) } "#, ); @@ -367,7 +367,7 @@ use test_mod::*; fn main() { let test_struct = test_mod::TestStruct {}; - TestTrait::test_method(test_struct, 12, 32u) + TestStruct::test_method(test_struct, 12, 32u) } "#, ); @@ -410,7 +410,7 @@ use test_mod::*; fn main() { let test_struct = test_mod::TestStruct {}; - TestTrait::test_method(&mut test_struct, 12, 32u) + TestStruct::test_method(&mut test_struct, 12, 32u) } "#, ); @@ -480,7 +480,7 @@ use test_mod::*; fn main() { let test_struct = TestStruct {}; - TestTrait::test_method::<()>(&test_struct) + TestStruct::test_method::<()>(&test_struct) } "#, ); diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 8f83496e93..505ebc98f3 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -244,7 +244,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)?; @@ -271,15 +271,43 @@ fn rename_reference( } } + let def = convert_to_trait_def(def, sema); + let usages = def.usages(sema).all(); + + if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { + cov_mark::hit!(rename_underscore_multiple); + bail!("Cannot rename reference to `_` as it is being referenced multiple times"); + } + let mut source_change = SourceChange::default(); + source_change.extend(usages.iter().map(|(&file_id, references)| { + (file_id, source_edit_from_references(references, def, new_name)) + })); + + let mut insert_def_edit = |def| { + let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; + source_change.insert_source_edit(file_id, edit); + Ok(()) + }; + match def { + Definition::Local(l) => l + .associated_locals(sema.db) + .iter() + .try_for_each(|&local| insert_def_edit(Definition::Local(local))), + def => insert_def_edit(def), + }?; + Ok(source_change) +} + +pub(crate) fn convert_to_trait_def(def: Definition, sema: &Semantics) -> Definition { + // HACK: resolve trait impl items to the item def of the trait definition + // so that we properly resolve all trait item references 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 { + match assoc_item { Some(assoc) => assoc .containing_trait_impl(sema.db) .and_then(|trait_| { @@ -305,31 +333,7 @@ fn rename_reference( }) .unwrap_or(def), None => def, - }; - let usages = def.usages(sema).all(); - - if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { - cov_mark::hit!(rename_underscore_multiple); - bail!("Cannot rename reference to `_` as it is being referenced multiple times"); } - let mut source_change = SourceChange::default(); - source_change.extend(usages.iter().map(|(&file_id, references)| { - (file_id, source_edit_from_references(references, def, new_name)) - })); - - let mut insert_def_edit = |def| { - let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; - source_change.insert_source_edit(file_id, edit); - Ok(()) - }; - match def { - Definition::Local(l) => l - .associated_locals(sema.db) - .iter() - .try_for_each(|&local| insert_def_edit(Definition::Local(local))), - def => insert_def_edit(def), - }?; - Ok(source_change) } pub fn source_edit_from_references( diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index e6bd46347d..25065ba7e3 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -619,7 +619,9 @@ impl<'a> FindUsages<'a> { }; sink(file_id, reference) } - Some(NameRefClass::Definition(def)) if def == self.def => { + Some(NameRefClass::Definition(def)) + if crate::rename::convert_to_trait_def(def, self.sema) == self.def => + { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { range, diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 0716bae751..885aef1668 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -121,6 +121,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let Packed { a: ref _a } = packed; // unsafe auto ref of packed field - packed.a.calls_autoref(); + packed.a.calls_autoref(); } } \ No newline at end of file From 9ea8d5806d8adb8d717247f157651d4ed5ab8d33 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Fri, 24 Jun 2022 22:17:26 +0800 Subject: [PATCH 08/16] fix test in qualify_method: stay in trait path --- .../src/handlers/qualify_method_call.rs | 44 ++++++++++++++----- crates/ide-db/src/imports/import_assets.rs | 2 +- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs index 7d642e45e8..71d3e0b212 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,33 @@ 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_) => { + let impled_trait = if matches!(assoc_item, AssocItem::Function(..)) { + impl_.trait_(db) + } else { + None + }; + match impled_trait { + None => ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)), + Some(t) => ItemInNs::from(ModuleDef::from(t)), + } + } + }, + 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::*; @@ -281,7 +305,7 @@ use test_mod::*; fn main() { let test_struct = test_mod::TestStruct {}; - TestStruct::test_method(&test_struct) + TestTrait::test_method(&test_struct) } "#, ); @@ -324,7 +348,7 @@ use test_mod::*; fn main() { let test_struct = test_mod::TestStruct {}; - TestStruct::test_method(&test_struct, 12, 32u) + TestTrait::test_method(&test_struct, 12, 32u) } "#, ); @@ -367,7 +391,7 @@ use test_mod::*; fn main() { let test_struct = test_mod::TestStruct {}; - TestStruct::test_method(test_struct, 12, 32u) + TestTrait::test_method(test_struct, 12, 32u) } "#, ); @@ -410,7 +434,7 @@ use test_mod::*; fn main() { let test_struct = test_mod::TestStruct {}; - TestStruct::test_method(&mut test_struct, 12, 32u) + TestTrait::test_method(&mut test_struct, 12, 32u) } "#, ); @@ -480,7 +504,7 @@ use test_mod::*; fn main() { let test_struct = TestStruct {}; - TestStruct::test_method::<()>(&test_struct) + TestTrait::test_method::<()>(&test_struct) } "#, ); diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 81467ab07a..08b0f9535d 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -401,7 +401,7 @@ fn import_for_item( }) } -pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option { +fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option { Some(match item { ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) { Some(assoc_item) => match assoc_item.container(db) { From 353829fc4ef84b92b2fd3c7bda7cf267208fba7d Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Fri, 24 Jun 2022 22:41:07 +0800 Subject: [PATCH 09/16] highlight: trait path --- .../src/handlers/qualify_method_call.rs | 15 ++++----------- crates/ide/src/syntax_highlighting/highlight.rs | 6 +++++- .../test_data/highlight_unsafe.html | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs index 71d3e0b212..61cb2f0479 100644 --- a/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -69,17 +69,10 @@ fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option 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_) => { - let impled_trait = if matches!(assoc_item, AssocItem::Function(..)) { - impl_.trait_(db) - } else { - None - }; - match impled_trait { - None => ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)), - Some(t) => ItemInNs::from(ModuleDef::from(t)), - } - } + 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, }, diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 4c5bdfd967..121ddbc24b 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/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 885aef1668..0716bae751 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -121,6 +121,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let Packed { a: ref _a } = packed; // unsafe auto ref of packed field - packed.a.calls_autoref(); + packed.a.calls_autoref(); } } \ No newline at end of file From 900b4208fd18800b27035c80581b7e7f3dbba39d Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Fri, 24 Jun 2022 22:42:30 +0800 Subject: [PATCH 10/16] rename "trait_def" to "def_in_trait" --- crates/ide-db/src/rename.rs | 7 +++++-- crates/ide-db/src/search.rs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 505ebc98f3..bade88c578 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -271,7 +271,7 @@ fn rename_reference( } } - let def = convert_to_trait_def(def, sema); + let def = convert_to_def_in_trait(def, sema); let usages = def.usages(sema).all(); if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { @@ -298,7 +298,10 @@ fn rename_reference( Ok(source_change) } -pub(crate) fn convert_to_trait_def(def: Definition, sema: &Semantics) -> Definition { +pub(crate) fn convert_to_def_in_trait( + def: Definition, + sema: &Semantics, +) -> Definition { // HACK: resolve trait impl items to the item def of the trait definition // so that we properly resolve all trait item references let assoc_item = match def { diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 25065ba7e3..ce38fe145b 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -620,7 +620,7 @@ impl<'a> FindUsages<'a> { sink(file_id, reference) } Some(NameRefClass::Definition(def)) - if crate::rename::convert_to_trait_def(def, self.sema) == self.def => + if crate::rename::convert_to_def_in_trait(def, self.sema) == self.def => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { From e3b1303c4f44060892653e319ca18a16e179f059 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Sat, 25 Jun 2022 01:02:39 +0800 Subject: [PATCH 11/16] fix test in resolve_proc_macro: Foo is Owner --- crates/rust-analyzer/tests/slow-tests/main.rs | 2 +- crates/test-utils/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 6d2788d337..884224960f 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 diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 4438a12093..405c4e26f2 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -389,7 +389,7 @@ fn main() { /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag /// that slow tests did run. pub fn skip_slow_tests() -> bool { - let should_skip = std::env::var("CI").is_err() && std::env::var("RUN_SLOW_TESTS").is_err(); + let should_skip = std::env::var("RUN_SLOW_TESTS").is_err(); if should_skip { eprintln!("ignoring slow test"); } else { From 82c1b313bcfaa5d0e4e7126feda0e9b9b9746560 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Sat, 25 Jun 2022 10:25:56 +0800 Subject: [PATCH 12/16] restore sth --- crates/test-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 405c4e26f2..4438a12093 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -389,7 +389,7 @@ fn main() { /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag /// that slow tests did run. pub fn skip_slow_tests() -> bool { - let should_skip = std::env::var("RUN_SLOW_TESTS").is_err(); + let should_skip = std::env::var("CI").is_err() && std::env::var("RUN_SLOW_TESTS").is_err(); if should_skip { eprintln!("ignoring slow test"); } else { From 0dbc091fee10cf8aae68d971713b32c07d1a96f0 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Sat, 25 Jun 2022 17:33:27 +0800 Subject: [PATCH 13/16] add test for suggest_name --- crates/hir/src/semantics.rs | 12 ++--------- crates/hir/src/source_analyzer.rs | 22 ++++++++++++++++---- crates/ide-assists/src/utils/suggest_name.rs | 15 +++++++++++++ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index aa10b0f878..744f3865aa 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -16,7 +16,6 @@ use hir_expand::{ 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 @@ impl<'db> SemanticsImpl<'db> { } 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 3faae8fc4b..7e584aa2b4 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -43,8 +43,8 @@ use syntax::{ 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 @@ -239,15 +239,29 @@ impl SourceAnalyzer { ) } + 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())?; 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); - Some((f_in_impl.unwrap_or(f_in_trait), substs)) + f_in_impl.or(Some(f_in_trait)) } pub(crate) fn resolve_field( diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index 5b79a7495f..b05a41fa4b 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -450,6 +450,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( From 408fa148b9690387316115af33d80c997128b2c5 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Sun, 26 Jun 2022 22:48:26 +0800 Subject: [PATCH 14/16] add test for item same name --- crates/ide/src/goto_definition.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index e44b5020c3..fb88d1ec84 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,4 +1,4 @@ -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, AssocItem, Semantics}; @@ -126,9 +126,11 @@ fn try_filter_trait_item_definition( }; 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]) } @@ -175,6 +177,23 @@ 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( From dcb4837b2d07aeac2126dc6312a7a48992d11b85 Mon Sep 17 00:00:00 2001 From: bitgaoshu Date: Sun, 26 Jun 2022 23:09:06 +0800 Subject: [PATCH 15/16] WellFormed -> Holds --- crates/hir-ty/src/method_resolution.rs | 2 +- crates/ide/src/goto_definition.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 76c76ee6cf..d2081d22d7 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -615,7 +615,7 @@ fn find_matching_impl( let wh_goals = crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs) .into_iter() - .map(|b| b.into_well_formed_goal(Interner).cast(Interner)); + .map(|b| b.cast(Interner)); let goal = crate::Goal::all(Interner, wh_goals); diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index fb88d1ec84..e10789fd47 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1445,6 +1445,32 @@ fn f() { "#, ); } + #[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() { From 38c11bea217847d81da397881f754ae8a8e2725a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 18 Jul 2022 18:23:50 +0200 Subject: [PATCH 16/16] Move `convert_to_def_in_trait` into `ide-db` --- crates/ide-db/src/imports/import_assets.rs | 2 +- crates/ide-db/src/rename.rs | 46 ++-------------------- crates/ide-db/src/search.rs | 3 +- crates/ide-db/src/traits.rs | 26 +++++++++++- 4 files changed, 30 insertions(+), 47 deletions(-) diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 08b0f9535d..81467ab07a 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -401,7 +401,7 @@ fn import_for_item( }) } -fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option { +pub fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option { Some(match item { ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) { Some(assoc_item) => match assoc_item.container(db) { diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index bade88c578..f114d934e4 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::{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 @@ use crate::{ search::FileReference, source_change::{FileSystemEdit, SourceChange}, syntax_helpers::node_ext::expr_as_name_ref, + traits::convert_to_def_in_trait, RootDatabase, }; @@ -271,7 +272,7 @@ fn rename_reference( } } - let def = convert_to_def_in_trait(def, sema); + let def = convert_to_def_in_trait(sema.db, def); let usages = def.usages(sema).all(); if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { @@ -298,47 +299,6 @@ fn rename_reference( Ok(source_change) } -pub(crate) fn convert_to_def_in_trait( - def: Definition, - sema: &Semantics, -) -> Definition { - // HACK: resolve trait impl items to the item def of the trait definition - // so that we properly resolve all trait item references - let assoc_item = match def { - 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, - }; - 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, - } -} - pub fn source_edit_from_references( references: &[FileReference], def: Definition, diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index ce38fe145b..b3b957c0ef 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -16,6 +16,7 @@ use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; use crate::{ defs::{Definition, NameClass, NameRefClass}, + traits::convert_to_def_in_trait, RootDatabase, }; @@ -620,7 +621,7 @@ impl<'a> FindUsages<'a> { sink(file_id, reference) } Some(NameRefClass::Definition(def)) - if crate::rename::convert_to_def_in_trait(def, self.sema) == self.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 { diff --git a/crates/ide-db/src/traits.rs b/crates/ide-db/src/traits.rs index 0fbfd86992..666499ed7a 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};