mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +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 =
|
let self_ty_with_vars =
|
||||||
Canonical { num_vars: vars.len() + self_ty.num_vars, value: 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);
|
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
|
// 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()));
|
// Also, if any of the vars we added are still in there, we replace them by
|
||||||
result
|
// 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(
|
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