diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs | 110 |
1 files changed, 76 insertions, 34 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 9a63d5013..3a1a3f4fd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -1,7 +1,7 @@ //! This module is concerned with finding methods that a given type provides. //! For details about how this works in rustc, see the method lookup page in the //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) -//! and the corresponding code mostly in librustc_typeck/check/method/probe.rs. +//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. use std::{iter, ops::ControlFlow, sync::Arc}; use arrayvec::ArrayVec; @@ -654,7 +654,7 @@ fn find_matching_impl( 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(); + TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build(); let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs); table @@ -914,22 +914,10 @@ fn iterate_trait_method_candidates( let db = table.db; let env = table.trait_env.clone(); let self_is_array = matches!(self_ty.kind(Interner), chalk_ir::TyKind::Array(..)); - // if ty is `dyn Trait`, the trait doesn't need to be in scope - let inherent_trait = - self_ty.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); - let env_traits = matches!(self_ty.kind(Interner), TyKind::Placeholder(_)) - // if we have `T: Trait` in the param env, the trait doesn't need to be in scope - .then(|| { - env.traits_in_scope_from_clauses(self_ty.clone()) - .flat_map(|t| all_super_traits(db.upcast(), t)) - }) - .into_iter() - .flatten(); - let traits = inherent_trait.chain(env_traits).chain(traits_in_scope.iter().copied()); let canonical_self_ty = table.canonicalize(self_ty.clone()).value; - 'traits: for t in traits { + 'traits: for &t in traits_in_scope { let data = db.trait_data(t); // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during @@ -979,6 +967,44 @@ fn iterate_inherent_methods( ) -> ControlFlow<()> { let db = table.db; let env = table.trait_env.clone(); + + // For trait object types and placeholder types with trait bounds, the methods of the trait and + // its super traits are considered inherent methods. This matters because these methods have + // higher priority than the other traits' methods, which would be considered in + // `iterate_trait_method_candidates()` only after this function. + match self_ty.kind(Interner) { + TyKind::Placeholder(_) => { + let env = table.trait_env.clone(); + let traits = env + .traits_in_scope_from_clauses(self_ty.clone()) + .flat_map(|t| all_super_traits(db.upcast(), t)); + iterate_inherent_trait_methods( + self_ty, + table, + name, + receiver_ty, + receiver_adjustments.clone(), + callback, + traits, + )?; + } + TyKind::Dyn(_) => { + if let Some(principal_trait) = self_ty.dyn_trait() { + let traits = all_super_traits(db.upcast(), principal_trait); + iterate_inherent_trait_methods( + self_ty, + table, + name, + receiver_ty, + receiver_adjustments.clone(), + callback, + traits.into_iter(), + )?; + } + } + _ => {} + } + let def_crates = match def_crates(db, self_ty, env.krate) { Some(k) => k, None => return ControlFlow::Continue(()), @@ -1020,6 +1046,28 @@ fn iterate_inherent_methods( } return ControlFlow::Continue(()); + fn iterate_inherent_trait_methods( + self_ty: &Ty, + table: &mut InferenceTable<'_>, + name: Option<&Name>, + receiver_ty: Option<&Ty>, + receiver_adjustments: Option<ReceiverAdjustments>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + traits: impl Iterator<Item = TraitId>, + ) -> ControlFlow<()> { + let db = table.db; + for t in traits { + let data = db.trait_data(t); + for &(_, item) in data.items.iter() { + // We don't pass `visible_from_module` as all trait items should be visible. + if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { + callback(receiver_adjustments.clone().unwrap_or_default(), item)?; + } + } + } + ControlFlow::Continue(()) + } + fn impls_for_self_ty( impls: &InherentImpls, self_ty: &Ty, @@ -1099,10 +1147,9 @@ fn is_valid_candidate( })); 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); + let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id) + .fill_with_inference_vars(table) + .build(); table.unify(&expected_self_ty, &self_ty) }); if !self_ty_matches { @@ -1138,31 +1185,26 @@ fn is_valid_fn_candidate( table.run_in_snapshot(|table| { let container = fn_id.lookup(db.upcast()).container; - let impl_subst = match container { + let (impl_subst, expect_self_ty) = match container { ItemContainerId::ImplId(it) => { - TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build() + let subst = + TyBuilder::subst_for_def(db, it, None).fill_with_inference_vars(table).build(); + let self_ty = db.impl_self_ty(it).substitute(Interner, &subst); + (subst, self_ty) } ItemContainerId::TraitId(it) => { - TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build() + let subst = + TyBuilder::subst_for_def(db, it, None).fill_with_inference_vars(table).build(); + let self_ty = subst.at(Interner, 0).assert_ty_ref(Interner).clone(); + (subst, self_ty) } _ => unreachable!(), }; - let fn_subst = TyBuilder::subst_for_def(db, fn_id) - .use_parent_substs(&impl_subst) + let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone())) .fill_with_inference_vars(table) .build(); - let expect_self_ty = match container { - ItemContainerId::TraitId(_) => fn_subst.at(Interner, 0).assert_ty_ref(Interner).clone(), - ItemContainerId::ImplId(impl_id) => { - fn_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!() - } - }; check_that!(table.unify(&expect_self_ty, self_ty)); if let Some(receiver_ty) = receiver_ty { |