diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 4ced30c81dce..0213bd904b68 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -241,7 +241,7 @@ pub type StaticLoc = AssocItemLoc; impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static); impl_loc!(StaticLoc, id: Static, container: ItemContainerId); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct TraitId(salsa::InternId); pub type TraitLoc = ItemLoc; impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait); diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 3ddfea13ab0c..d08f9b7ff06f 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -35,7 +35,7 @@ use crate::{ }; /// This is used as a key for indexing impls. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum TyFingerprint { // These are lang item impls: Str, diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 158dbaf1b1dd..8350fdcc4c07 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -1633,3 +1633,39 @@ fn main() { "#]], ); } + +#[test] +fn trait_impl_on_slice_method_on_deref_slice_type() { + check( + r#" +//- minicore: deref, sized +struct SliceDeref; +impl core::ops::Deref for SliceDeref { + type Target = [()]; + + fn deref(&self) -> &Self::Target { + &[] + } +} +fn main() { + SliceDeref.choose$0(); +} +mod module { + pub(super) trait SliceRandom { + type Item; + + fn choose(&self); + } + + impl SliceRandom for [T] { + type Item = T; + + fn choose(&self) {} + } +} +"#, + expect![[r#" + me choose (use module::SliceRandom) fn(&self) + "#]], + ); +} diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 07e197334706..82a182806a4c 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -531,40 +531,61 @@ fn trait_applicable_items( }) .collect(); - trait_candidates.retain(|&candidate_trait_id| { - // we care about the following cases: - // 1. Trait's definition crate - // 2. Definition crates for all trait's generic arguments - // a. This is recursive for fundamental types: `Into> for ()`` is OK, but - // `Into> for ()`` is *not*. - // 3. Receiver type definition crate - // a. This is recursive for fundamental types - let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db); - let Some(receiver) = trait_candidate.receiver_ty.fingerprint_for_trait_impl() else { - return false; - }; - - // in order to handle implied bounds through an associated type, keep any - // method receiver that matches `TyFingerprint::Unnameable`. this receiver - // won't be in `TraitImpls` anyways, as `TraitImpls` only contains actual - // implementations. - if matches!(receiver, TyFingerprint::Unnameable) { - return true; + let autoderef_method_receiver = { + let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::>(); + // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) + if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) { + let slice = Type::new_slice(ty); + deref_chain.push(slice); } + deref_chain + .into_iter() + .filter_map(|ty| Some((ty.krate(db).into(), ty.fingerprint_for_trait_impl()?))) + .sorted() + .unique() + .collect::>() + }; - let definitions_exist_in_trait_crate = db - .trait_impls_in_crate(defining_crate_for_trait.into()) - .has_impls_for_trait_and_self_ty(candidate_trait_id, receiver); + // can be empty if the entire deref chain is has no valid trait impl fingerprints + if autoderef_method_receiver.is_empty() { + return Default::default(); + } - // this is a closure for laziness: if `definitions_exist_in_trait_crate` is true, - // we can avoid a second db lookup. - let definitions_exist_in_receiver_crate = || { - db.trait_impls_in_crate(trait_candidate.receiver_ty.krate(db).into()) - .has_impls_for_trait_and_self_ty(candidate_trait_id, receiver) - }; + // in order to handle implied bounds through an associated type, keep all traits if any + // type in the deref chain matches `TyFingerprint::Unnameable`. This fingerprint + // won't be in `TraitImpls` anyways, as `TraitImpls` only contains actual implementations. + if !autoderef_method_receiver + .iter() + .any(|(_, fingerprint)| matches!(fingerprint, TyFingerprint::Unnameable)) + { + trait_candidates.retain(|&candidate_trait_id| { + // we care about the following cases: + // 1. Trait's definition crate + // 2. Definition crates for all trait's generic arguments + // a. This is recursive for fundamental types: `Into> for ()`` is OK, but + // `Into> for ()`` is *not*. + // 3. Receiver type definition crate + // a. This is recursive for fundamental types + let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db); + + let trait_impls_in_crate = db.trait_impls_in_crate(defining_crate_for_trait.into()); + let definitions_exist_in_trait_crate = + autoderef_method_receiver.iter().any(|&(_, fingerprint)| { + trait_impls_in_crate + .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint) + }); + // this is a closure for laziness: if `definitions_exist_in_trait_crate` is true, + // we can avoid a second db lookup. + let definitions_exist_in_receiver_crate = || { + autoderef_method_receiver.iter().any(|&(krate, fingerprint)| { + db.trait_impls_in_crate(krate) + .has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint) + }) + }; - definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate() - }); + definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate() + }); + } let mut located_imports = FxIndexSet::default(); let mut trait_import_paths = FxHashMap::default(); diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs index c594754bd440..84007b16aa67 100644 --- a/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -23,7 +23,7 @@ pub(crate) fn unused_variables( return None; } let diagnostic_range = ctx.sema.diagnostics_display_range(ast); - // The range for the Actual Name. We don't want to replace the entire declarition. Using the diagnostic range causes issues within in Array Destructuring. + // The range for the Actual Name. We don't want to replace the entire declaration. Using the diagnostic range causes issues within in Array Destructuring. let name_range = d .local .primary_source(ctx.sema.db)