Skip to content

Commit 6b3659d

Browse files
committed
Auto merge of #15026 - lowr:fix/deduplicate-compl-fields, r=Veykril
fix: deduplicate fields and types in completion Fixes #15024 - `hir_ty::autoderef()` (which is only meant to be used outside `hir-ty`) now deduplicates types and completely resolves inference variables within. - field completion now deduplicates fields of the same name and only picks such field of the first type in the deref chain.
2 parents dcd3155 + 42eab5e commit 6b3659d

File tree

3 files changed

+118
-6
lines changed

3 files changed

+118
-6
lines changed

crates/hir-ty/src/autoderef.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,37 @@ pub(crate) enum AutoderefKind {
2222
Overloaded,
2323
}
2424

25+
/// Returns types that `ty` transitively dereferences to. This function is only meant to be used
26+
/// outside `hir-ty`.
27+
///
28+
/// It is guaranteed that:
29+
/// - the yielded types don't contain inference variables (but may contain `TyKind::Error`).
30+
/// - a type won't be yielded more than once; in other words, the returned iterator will stop if it
31+
/// detects a cycle in the deref chain.
2532
pub fn autoderef(
2633
db: &dyn HirDatabase,
2734
env: Arc<TraitEnvironment>,
2835
ty: Canonical<Ty>,
29-
) -> impl Iterator<Item = Canonical<Ty>> + '_ {
36+
) -> impl Iterator<Item = Ty> {
3037
let mut table = InferenceTable::new(db, env);
3138
let ty = table.instantiate_canonical(ty);
3239
let mut autoderef = Autoderef::new(&mut table, ty);
3340
let mut v = Vec::new();
3441
while let Some((ty, _steps)) = autoderef.next() {
35-
v.push(autoderef.table.canonicalize(ty).value);
42+
// `ty` may contain unresolved inference variables. Since there's no chance they would be
43+
// resolved, just replace with fallback type.
44+
let resolved = autoderef.table.resolve_completely(ty);
45+
46+
// If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
47+
// would revisit some already visited types. Stop here to avoid duplication.
48+
//
49+
// XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't
50+
// be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more
51+
// performant.
52+
if v.contains(&resolved) {
53+
break;
54+
}
55+
v.push(resolved);
3656
}
3757
v.into_iter()
3858
}

crates/hir/src/lib.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -3818,14 +3818,16 @@ impl Type {
38183818
}
38193819
}
38203820

3821-
pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
3821+
/// Returns types that this type dereferences to (including this type itself). The returned
3822+
/// iterator won't yield the same type more than once even if the deref chain contains a cycle.
3823+
pub fn autoderef(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Type> + '_ {
38223824
self.autoderef_(db).map(move |ty| self.derived(ty))
38233825
}
38243826

3825-
fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
3827+
fn autoderef_(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Ty> {
38263828
// There should be no inference vars in types passed here
38273829
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
3828-
autoderef(db, self.env.clone(), canonical).map(|canonical| canonical.value)
3830+
autoderef(db, self.env.clone(), canonical)
38293831
}
38303832

38313833
// This would be nicer if it just returned an iterator, but that runs into

crates/ide-completion/src/completions/dot.rs

+91-1
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,12 @@ fn complete_fields(
105105
mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type),
106106
mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type),
107107
) {
108+
let mut seen_names = FxHashSet::default();
108109
for receiver in receiver.autoderef(ctx.db) {
109110
for (field, ty) in receiver.fields(ctx.db) {
110-
named_field(acc, field, ty);
111+
if seen_names.insert(field.name(ctx.db)) {
112+
named_field(acc, field, ty);
113+
}
111114
}
112115
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
113116
// Tuple fields are always public (tuple struct fields are handled above).
@@ -671,6 +674,52 @@ impl T {
671674
);
672675
}
673676

677+
#[test]
678+
fn test_field_no_same_name() {
679+
check(
680+
r#"
681+
//- minicore: deref
682+
struct A { field: u8 }
683+
struct B { field: u16, another: u32 }
684+
impl core::ops::Deref for A {
685+
type Target = B;
686+
fn deref(&self) -> &Self::Target { loop {} }
687+
}
688+
fn test(a: A) {
689+
a.$0
690+
}
691+
"#,
692+
expect![[r#"
693+
fd another u32
694+
fd field u8
695+
me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
696+
"#]],
697+
);
698+
}
699+
700+
#[test]
701+
fn test_tuple_field_no_same_index() {
702+
check(
703+
r#"
704+
//- minicore: deref
705+
struct A(u8);
706+
struct B(u16, u32);
707+
impl core::ops::Deref for A {
708+
type Target = B;
709+
fn deref(&self) -> &Self::Target { loop {} }
710+
}
711+
fn test(a: A) {
712+
a.$0
713+
}
714+
"#,
715+
expect![[r#"
716+
fd 0 u8
717+
fd 1 u32
718+
me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
719+
"#]],
720+
);
721+
}
722+
674723
#[test]
675724
fn test_completion_works_in_consts() {
676725
check(
@@ -979,4 +1028,45 @@ fn test(thing: impl Encrypt) {
9791028
"#]],
9801029
)
9811030
}
1031+
1032+
#[test]
1033+
fn only_consider_same_type_once() {
1034+
check(
1035+
r#"
1036+
//- minicore: deref
1037+
struct A(u8);
1038+
struct B(u16);
1039+
impl core::ops::Deref for A {
1040+
type Target = B;
1041+
fn deref(&self) -> &Self::Target { loop {} }
1042+
}
1043+
impl core::ops::Deref for B {
1044+
type Target = A;
1045+
fn deref(&self) -> &Self::Target { loop {} }
1046+
}
1047+
fn test(a: A) {
1048+
a.$0
1049+
}
1050+
"#,
1051+
expect![[r#"
1052+
fd 0 u8
1053+
me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
1054+
"#]],
1055+
);
1056+
}
1057+
1058+
#[test]
1059+
fn no_inference_var_in_completion() {
1060+
check(
1061+
r#"
1062+
struct S<T>(T);
1063+
fn test(s: S<Unknown>) {
1064+
s.$0
1065+
}
1066+
"#,
1067+
expect![[r#"
1068+
fd 0 {unknown}
1069+
"#]],
1070+
);
1071+
}
9821072
}

0 commit comments

Comments
 (0)