mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
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>
This commit is contained in:
commit
0714a065d5
2 changed files with 56 additions and 3 deletions
|
@ -516,9 +516,31 @@ pub(crate) fn inherent_impl_substs(
|
|||
let self_ty_with_vars =
|
||||
Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars };
|
||||
let substs = super::infer::unify(&self_ty_with_vars, self_ty);
|
||||
// we only want the substs for the vars we added, not the ones from self_ty
|
||||
let result = substs.map(|s| s.suffix(vars.len()));
|
||||
result
|
||||
// We only want the substs for the vars we added, not the ones from self_ty.
|
||||
// Also, if any of the vars we added are still in there, we replace them by
|
||||
// Unknown. I think this can only really happen if self_ty contained
|
||||
// Unknown, and in that case we want the result to contain Unknown in those
|
||||
// places again.
|
||||
substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.num_vars))
|
||||
}
|
||||
|
||||
/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
|
||||
/// num_vars_to_keep) by `Ty::Unknown`.
|
||||
fn fallback_bound_vars(s: Substs, num_vars_to_keep: usize) -> Substs {
|
||||
s.fold_binders(
|
||||
&mut |ty, binders| {
|
||||
if let Ty::Bound(idx) = &ty {
|
||||
if *idx >= binders as u32 {
|
||||
Ty::Unknown
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
},
|
||||
num_vars_to_keep,
|
||||
)
|
||||
}
|
||||
|
||||
fn transform_receiver_ty(
|
||||
|
|
|
@ -718,4 +718,35 @@ mod tests {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_completion_3547() {
|
||||
assert_debug_snapshot!(
|
||||
do_ref_completion(
|
||||
r"
|
||||
struct HashSet<T> {}
|
||||
impl<T> HashSet<T> {
|
||||
pub fn the_method(&self) {}
|
||||
}
|
||||
fn foo() {
|
||||
let s: HashSet<_>;
|
||||
s.<|>
|
||||
}
|
||||
",
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "the_method()",
|
||||
source_range: [201; 201),
|
||||
delete: [201; 201),
|
||||
insert: "the_method()$0",
|
||||
kind: Method,
|
||||
lookup: "the_method",
|
||||
detail: "pub fn the_method(&self)",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue