Skip to content

Commit 0714a06

Browse files
Merge #3552
3552: Fix completion with a partially unknown type r=matklad a=flodiebold To test whether the receiver type matches for the impl, we unify the given self type (in this case `HashSet<{unknown}>`) with the self type of the impl (`HashSet<?0>`), but if the given self type contains Unknowns, they won't be unified with the variables in those places. So we got a receiver type that was different from the expected one, and concluded the impl doesn't match. The fix is slightly hacky; if after the unification, our variables are still there, we make them fall back to Unknown. This does make some sense though, since we don't want to 'leak' the variables. Fixes #3547. Co-authored-by: Florian Diebold <flodiebold@gmail.com>
2 parents e5df8c4 + adc7b8e commit 0714a06

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

crates/ra_hir_ty/src/method_resolution.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -516,9 +516,31 @@ pub(crate) fn inherent_impl_substs(
516516
let self_ty_with_vars =
517517
Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars };
518518
let substs = super::infer::unify(&self_ty_with_vars, self_ty);
519-
// we only want the substs for the vars we added, not the ones from self_ty
520-
let result = substs.map(|s| s.suffix(vars.len()));
521-
result
519+
// We only want the substs for the vars we added, not the ones from self_ty.
520+
// Also, if any of the vars we added are still in there, we replace them by
521+
// Unknown. I think this can only really happen if self_ty contained
522+
// Unknown, and in that case we want the result to contain Unknown in those
523+
// places again.
524+
substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.num_vars))
525+
}
526+
527+
/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
528+
/// num_vars_to_keep) by `Ty::Unknown`.
529+
fn fallback_bound_vars(s: Substs, num_vars_to_keep: usize) -> Substs {
530+
s.fold_binders(
531+
&mut |ty, binders| {
532+
if let Ty::Bound(idx) = &ty {
533+
if *idx >= binders as u32 {
534+
Ty::Unknown
535+
} else {
536+
ty
537+
}
538+
} else {
539+
ty
540+
}
541+
},
542+
num_vars_to_keep,
543+
)
522544
}
523545

524546
fn transform_receiver_ty(

crates/ra_ide/src/completion/complete_dot.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,4 +718,35 @@ mod tests {
718718
"###
719719
);
720720
}
721+
722+
#[test]
723+
fn test_method_completion_3547() {
724+
assert_debug_snapshot!(
725+
do_ref_completion(
726+
r"
727+
struct HashSet<T> {}
728+
impl<T> HashSet<T> {
729+
pub fn the_method(&self) {}
730+
}
731+
fn foo() {
732+
let s: HashSet<_>;
733+
s.<|>
734+
}
735+
",
736+
),
737+
@r###"
738+
[
739+
CompletionItem {
740+
label: "the_method()",
741+
source_range: [201; 201),
742+
delete: [201; 201),
743+
insert: "the_method()$0",
744+
kind: Method,
745+
lookup: "the_method",
746+
detail: "pub fn the_method(&self)",
747+
},
748+
]
749+
"###
750+
);
751+
}
721752
}

0 commit comments

Comments
 (0)