fix: Honor ref expressions for compute_ref_match completions

This commit is contained in:
Lukas Wirth 2022-07-27 13:48:26 +02:00
parent 6c379b9f4b
commit 1f8daa180f
4 changed files with 75 additions and 23 deletions

View file

@ -34,6 +34,7 @@ pub trait TyExt {
fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>; fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
fn strip_references(&self) -> &Ty; fn strip_references(&self) -> &Ty;
fn strip_reference(&self) -> &Ty;
/// If this is a `dyn Trait`, returns that trait. /// If this is a `dyn Trait`, returns that trait.
fn dyn_trait(&self) -> Option<TraitId>; fn dyn_trait(&self) -> Option<TraitId>;
@ -182,6 +183,10 @@ impl TyExt for Ty {
t t
} }
fn strip_reference(&self) -> &Ty {
self.as_reference().map_or(self, |(ty, _, _)| ty)
}
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> { fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
match self.kind(Interner) { match self.kind(Interner) {
TyKind::OpaqueType(opaque_ty_id, subst) => { TyKind::OpaqueType(opaque_ty_id, subst) => {

View file

@ -2769,6 +2769,10 @@ impl Type {
self.derived(self.ty.strip_references().clone()) self.derived(self.ty.strip_references().clone())
} }
pub fn strip_reference(&self) -> Type {
self.derived(self.ty.strip_reference().clone())
}
pub fn is_unknown(&self) -> bool { pub fn is_unknown(&self) -> bool {
self.ty.is_unknown() self.ty.is_unknown()
} }

View file

@ -162,11 +162,52 @@ impl<'a> CompletionContext<'a> {
} }
/// Calculate the expected type and name of the cursor position. /// Calculate the expected type and name of the cursor position.
fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) { fn expected_type_and_name(
&self,
name_like: &ast::NameLike,
) -> (Option<Type>, Option<NameOrNameRef>) {
let mut node = match self.token.parent() { let mut node = match self.token.parent() {
Some(it) => it, Some(it) => it,
None => return (None, None), None => return (None, None),
}; };
let strip_refs = |mut ty: Type| match name_like {
ast::NameLike::NameRef(n) => {
let p = match n.syntax().parent() {
Some(it) => it,
None => return ty,
};
let top_syn = match_ast! {
match p {
ast::FieldExpr(e) => e
.syntax()
.ancestors()
.map_while(ast::FieldExpr::cast)
.last()
.map(|it| it.syntax().clone()),
ast::PathSegment(e) => e
.syntax()
.ancestors()
.skip(1)
.take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind()))
.find_map(ast::PathExpr::cast)
.map(|it| it.syntax().clone()),
_ => None
}
};
let top_syn = match top_syn {
Some(it) => it,
None => return ty,
};
for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) {
cov_mark::hit!(expected_type_fn_param_ref);
ty = ty.strip_reference();
}
ty
}
_ => ty,
};
loop { loop {
break match_ast! { break match_ast! {
match node { match node {
@ -199,13 +240,9 @@ impl<'a> CompletionContext<'a> {
self.token.clone(), self.token.clone(),
).map(|ap| { ).map(|ap| {
let name = ap.ident().map(NameOrNameRef::Name); let name = ap.ident().map(NameOrNameRef::Name);
let ty = if has_ref(&self.token) {
cov_mark::hit!(expected_type_fn_param_ref); let ty = strip_refs(ap.ty);
ap.ty.remove_ref() (Some(ty), name)
} else {
Some(ap.ty)
};
(ty, name)
}) })
.unwrap_or((None, None)) .unwrap_or((None, None))
}, },
@ -330,8 +367,6 @@ impl<'a> CompletionContext<'a> {
return None; return None;
} }
(self.expected_type, self.expected_name) = self.expected_type_and_name();
// Overwrite the path kind for derives // Overwrite the path kind for derives
if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
if let Some(ast::NameLike::NameRef(name_ref)) = if let Some(ast::NameLike::NameRef(name_ref)) =
@ -389,6 +424,7 @@ impl<'a> CompletionContext<'a> {
return Some(analysis); return Some(analysis);
} }
}; };
(self.expected_type, self.expected_name) = self.expected_type_and_name(&name_like);
let analysis = match name_like { let analysis = match name_like {
ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime( ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime(
Self::classify_lifetime(&self.sema, original_file, lifetime)?, Self::classify_lifetime(&self.sema, original_file, lifetime)?,
@ -1141,19 +1177,6 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
Some((use_tree.path()?, true)) Some((use_tree.path()?, true))
} }
fn has_ref(token: &SyntaxToken) -> bool {
let mut token = token.clone();
for skip in [SyntaxKind::IDENT, SyntaxKind::WHITESPACE, T![mut]] {
if token.kind() == skip {
token = match token.prev_token() {
Some(it) => it,
None => return false,
}
}
}
token.kind() == T![&]
}
pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool { pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool {
// oh my ... // oh my ...
(|| { (|| {

View file

@ -391,3 +391,23 @@ fn foo($0: Foo) {}
expect![[r#"ty: ?, name: ?"#]], expect![[r#"ty: ?, name: ?"#]],
); );
} }
#[test]
fn expected_type_ref_prefix_on_field() {
check_expected_type_and_name(
r#"
fn foo(_: &mut i32) {}
struct S {
field: i32,
}
fn main() {
let s = S {
field: 100,
};
foo(&mut s.f$0);
}
"#,
expect!["ty: i32, name: ?"],
);
}