Skip to content

Commit cb32e26

Browse files
committed
Auto merge of rust-lang#12887 - Veykril:compl-pref-fix, r=Veykril
fix: Honor ref expressions for compute_ref_match completions Fixes rust-lang/rust-analyzer#8357
2 parents 8e4d9b8 + 1f8daa1 commit cb32e26

File tree

4 files changed

+75
-23
lines changed

4 files changed

+75
-23
lines changed

Diff for: crates/hir-ty/src/chalk_ext.rs

+5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub trait TyExt {
3434
fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
3535

3636
fn strip_references(&self) -> &Ty;
37+
fn strip_reference(&self) -> &Ty;
3738

3839
/// If this is a `dyn Trait`, returns that trait.
3940
fn dyn_trait(&self) -> Option<TraitId>;
@@ -182,6 +183,10 @@ impl TyExt for Ty {
182183
t
183184
}
184185

186+
fn strip_reference(&self) -> &Ty {
187+
self.as_reference().map_or(self, |(ty, _, _)| ty)
188+
}
189+
185190
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
186191
match self.kind(Interner) {
187192
TyKind::OpaqueType(opaque_ty_id, subst) => {

Diff for: crates/hir/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2769,6 +2769,10 @@ impl Type {
27692769
self.derived(self.ty.strip_references().clone())
27702770
}
27712771

2772+
pub fn strip_reference(&self) -> Type {
2773+
self.derived(self.ty.strip_reference().clone())
2774+
}
2775+
27722776
pub fn is_unknown(&self) -> bool {
27732777
self.ty.is_unknown()
27742778
}

Diff for: crates/ide-completion/src/context/analysis.rs

+46-23
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,52 @@ impl<'a> CompletionContext<'a> {
162162
}
163163

164164
/// Calculate the expected type and name of the cursor position.
165-
fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
165+
fn expected_type_and_name(
166+
&self,
167+
name_like: &ast::NameLike,
168+
) -> (Option<Type>, Option<NameOrNameRef>) {
166169
let mut node = match self.token.parent() {
167170
Some(it) => it,
168171
None => return (None, None),
169172
};
173+
174+
let strip_refs = |mut ty: Type| match name_like {
175+
ast::NameLike::NameRef(n) => {
176+
let p = match n.syntax().parent() {
177+
Some(it) => it,
178+
None => return ty,
179+
};
180+
let top_syn = match_ast! {
181+
match p {
182+
ast::FieldExpr(e) => e
183+
.syntax()
184+
.ancestors()
185+
.map_while(ast::FieldExpr::cast)
186+
.last()
187+
.map(|it| it.syntax().clone()),
188+
ast::PathSegment(e) => e
189+
.syntax()
190+
.ancestors()
191+
.skip(1)
192+
.take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind()))
193+
.find_map(ast::PathExpr::cast)
194+
.map(|it| it.syntax().clone()),
195+
_ => None
196+
}
197+
};
198+
let top_syn = match top_syn {
199+
Some(it) => it,
200+
None => return ty,
201+
};
202+
for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) {
203+
cov_mark::hit!(expected_type_fn_param_ref);
204+
ty = ty.strip_reference();
205+
}
206+
ty
207+
}
208+
_ => ty,
209+
};
210+
170211
loop {
171212
break match_ast! {
172213
match node {
@@ -199,13 +240,9 @@ impl<'a> CompletionContext<'a> {
199240
self.token.clone(),
200241
).map(|ap| {
201242
let name = ap.ident().map(NameOrNameRef::Name);
202-
let ty = if has_ref(&self.token) {
203-
cov_mark::hit!(expected_type_fn_param_ref);
204-
ap.ty.remove_ref()
205-
} else {
206-
Some(ap.ty)
207-
};
208-
(ty, name)
243+
244+
let ty = strip_refs(ap.ty);
245+
(Some(ty), name)
209246
})
210247
.unwrap_or((None, None))
211248
},
@@ -330,8 +367,6 @@ impl<'a> CompletionContext<'a> {
330367
return None;
331368
}
332369

333-
(self.expected_type, self.expected_name) = self.expected_type_and_name();
334-
335370
// Overwrite the path kind for derives
336371
if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
337372
if let Some(ast::NameLike::NameRef(name_ref)) =
@@ -389,6 +424,7 @@ impl<'a> CompletionContext<'a> {
389424
return Some(analysis);
390425
}
391426
};
427+
(self.expected_type, self.expected_name) = self.expected_type_and_name(&name_like);
392428
let analysis = match name_like {
393429
ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime(
394430
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)> {
11411177
Some((use_tree.path()?, true))
11421178
}
11431179

1144-
fn has_ref(token: &SyntaxToken) -> bool {
1145-
let mut token = token.clone();
1146-
for skip in [SyntaxKind::IDENT, SyntaxKind::WHITESPACE, T![mut]] {
1147-
if token.kind() == skip {
1148-
token = match token.prev_token() {
1149-
Some(it) => it,
1150-
None => return false,
1151-
}
1152-
}
1153-
}
1154-
token.kind() == T![&]
1155-
}
1156-
11571180
pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool {
11581181
// oh my ...
11591182
(|| {

Diff for: crates/ide-completion/src/context/tests.rs

+20
Original file line numberDiff line numberDiff line change
@@ -391,3 +391,23 @@ fn foo($0: Foo) {}
391391
expect![[r#"ty: ?, name: ?"#]],
392392
);
393393
}
394+
395+
#[test]
396+
fn expected_type_ref_prefix_on_field() {
397+
check_expected_type_and_name(
398+
r#"
399+
fn foo(_: &mut i32) {}
400+
struct S {
401+
field: i32,
402+
}
403+
404+
fn main() {
405+
let s = S {
406+
field: 100,
407+
};
408+
foo(&mut s.f$0);
409+
}
410+
"#,
411+
expect!["ty: i32, name: ?"],
412+
);
413+
}

0 commit comments

Comments
 (0)