Skip to content

Commit 606401f

Browse files
committed
fix: Fix trait method completions not acknowledging Deref impls
1 parent d9d8d94 commit 606401f

File tree

5 files changed

+90
-33
lines changed

5 files changed

+90
-33
lines changed

src/tools/rust-analyzer/crates/hir-def/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ pub type StaticLoc = AssocItemLoc<Static>;
241241
impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
242242
impl_loc!(StaticLoc, id: Static, container: ItemContainerId);
243243

244-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
244+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
245245
pub struct TraitId(salsa::InternId);
246246
pub type TraitLoc = ItemLoc<Trait>;
247247
impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);

src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::{
3535
};
3636

3737
/// This is used as a key for indexing impls.
38-
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
38+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3939
pub enum TyFingerprint {
4040
// These are lang item impls:
4141
Str,

src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,3 +1633,39 @@ fn main() {
16331633
"#]],
16341634
);
16351635
}
1636+
1637+
#[test]
1638+
fn trait_impl_on_slice_method_on_deref_slice_type() {
1639+
check(
1640+
r#"
1641+
//- minicore: deref, sized
1642+
struct SliceDeref;
1643+
impl core::ops::Deref for SliceDeref {
1644+
type Target = [()];
1645+
1646+
fn deref(&self) -> &Self::Target {
1647+
&[]
1648+
}
1649+
}
1650+
fn main() {
1651+
SliceDeref.choose$0();
1652+
}
1653+
mod module {
1654+
pub(super) trait SliceRandom {
1655+
type Item;
1656+
1657+
fn choose(&self);
1658+
}
1659+
1660+
impl<T> SliceRandom for [T] {
1661+
type Item = T;
1662+
1663+
fn choose(&self) {}
1664+
}
1665+
}
1666+
"#,
1667+
expect![[r#"
1668+
me choose (use module::SliceRandom) fn(&self)
1669+
"#]],
1670+
);
1671+
}

src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -531,40 +531,61 @@ fn trait_applicable_items(
531531
})
532532
.collect();
533533

534-
trait_candidates.retain(|&candidate_trait_id| {
535-
// we care about the following cases:
536-
// 1. Trait's definition crate
537-
// 2. Definition crates for all trait's generic arguments
538-
// a. This is recursive for fundamental types: `Into<Box<A>> for ()`` is OK, but
539-
// `Into<Vec<A>> for ()`` is *not*.
540-
// 3. Receiver type definition crate
541-
// a. This is recursive for fundamental types
542-
let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
543-
let Some(receiver) = trait_candidate.receiver_ty.fingerprint_for_trait_impl() else {
544-
return false;
545-
};
546-
547-
// in order to handle implied bounds through an associated type, keep any
548-
// method receiver that matches `TyFingerprint::Unnameable`. this receiver
549-
// won't be in `TraitImpls` anyways, as `TraitImpls` only contains actual
550-
// implementations.
551-
if matches!(receiver, TyFingerprint::Unnameable) {
552-
return true;
534+
let autoderef_method_receiver = {
535+
let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::<Vec<_>>();
536+
// As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
537+
if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) {
538+
let slice = Type::new_slice(ty);
539+
deref_chain.push(slice);
553540
}
541+
deref_chain
542+
.into_iter()
543+
.filter_map(|ty| Some((ty.krate(db).into(), ty.fingerprint_for_trait_impl()?)))
544+
.sorted()
545+
.unique()
546+
.collect::<Vec<_>>()
547+
};
554548

555-
let definitions_exist_in_trait_crate = db
556-
.trait_impls_in_crate(defining_crate_for_trait.into())
557-
.has_impls_for_trait_and_self_ty(candidate_trait_id, receiver);
549+
// can be empty if the entire deref chain is has no valid trait impl fingerprints
550+
if autoderef_method_receiver.is_empty() {
551+
return Default::default();
552+
}
558553

559-
// this is a closure for laziness: if `definitions_exist_in_trait_crate` is true,
560-
// we can avoid a second db lookup.
561-
let definitions_exist_in_receiver_crate = || {
562-
db.trait_impls_in_crate(trait_candidate.receiver_ty.krate(db).into())
563-
.has_impls_for_trait_and_self_ty(candidate_trait_id, receiver)
564-
};
554+
// in order to handle implied bounds through an associated type, keep all traits if any
555+
// type in the deref chain matches `TyFingerprint::Unnameable`. This fingerprint
556+
// won't be in `TraitImpls` anyways, as `TraitImpls` only contains actual implementations.
557+
if !autoderef_method_receiver
558+
.iter()
559+
.any(|(_, fingerprint)| matches!(fingerprint, TyFingerprint::Unnameable))
560+
{
561+
trait_candidates.retain(|&candidate_trait_id| {
562+
// we care about the following cases:
563+
// 1. Trait's definition crate
564+
// 2. Definition crates for all trait's generic arguments
565+
// a. This is recursive for fundamental types: `Into<Box<A>> for ()`` is OK, but
566+
// `Into<Vec<A>> for ()`` is *not*.
567+
// 3. Receiver type definition crate
568+
// a. This is recursive for fundamental types
569+
let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
570+
571+
let trait_impls_in_crate = db.trait_impls_in_crate(defining_crate_for_trait.into());
572+
let definitions_exist_in_trait_crate =
573+
autoderef_method_receiver.iter().any(|&(_, fingerprint)| {
574+
trait_impls_in_crate
575+
.has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
576+
});
577+
// this is a closure for laziness: if `definitions_exist_in_trait_crate` is true,
578+
// we can avoid a second db lookup.
579+
let definitions_exist_in_receiver_crate = || {
580+
autoderef_method_receiver.iter().any(|&(krate, fingerprint)| {
581+
db.trait_impls_in_crate(krate)
582+
.has_impls_for_trait_and_self_ty(candidate_trait_id, fingerprint)
583+
})
584+
};
565585

566-
definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
567-
});
586+
definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
587+
});
588+
}
568589

569590
let mut located_imports = FxIndexSet::default();
570591
let mut trait_import_paths = FxHashMap::default();

src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub(crate) fn unused_variables(
2323
return None;
2424
}
2525
let diagnostic_range = ctx.sema.diagnostics_display_range(ast);
26-
// 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.
26+
// 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.
2727
let name_range = d
2828
.local
2929
.primary_source(ctx.sema.db)

0 commit comments

Comments
 (0)