mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +00:00
fix: Honor ref expressions for compute_ref_match completions
This commit is contained in:
parent
6c379b9f4b
commit
1f8daa180f
4 changed files with 75 additions and 23 deletions
|
@ -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) => {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ...
|
||||||
(|| {
|
(|| {
|
||||||
|
|
|
@ -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: ?"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue