Skip to content

Commit 4ecaad9

Browse files
Merge #8056
8056: completion relevance consider if types can be unified r=JoshMcguigan a=JoshMcguigan This PR improves completion relevance scoring for generic types, in cases where the types could unify. ### Before ![pre-could-unify](https://user-images.githubusercontent.com/22216761/111338556-46d94e80-8634-11eb-9936-2b20eb9e6756.png) ### After ![post-could-unify](https://user-images.githubusercontent.com/22216761/111338598-4e005c80-8634-11eb-92e0-69c2c1cda6fc.png) changelog feature improve completions Co-authored-by: Josh Mcguigan <[email protected]>
2 parents 20e32fc + 0e31ae2 commit 4ecaad9

File tree

9 files changed

+124
-43
lines changed

9 files changed

+124
-43
lines changed

crates/hir/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use hir_def::{
5151
};
5252
use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
5353
use hir_ty::{
54-
autoderef,
54+
autoderef, could_unify,
5555
method_resolution::{self, TyFingerprint},
5656
primitive::UintTy,
5757
to_assoc_type_id,
@@ -2154,6 +2154,10 @@ impl Type {
21542154

21552155
walk_type(db, self, &mut cb);
21562156
}
2157+
2158+
pub fn could_unify_with(&self, other: &Type) -> bool {
2159+
could_unify(&self.ty, &other.ty)
2160+
}
21572161
}
21582162

21592163
// FIXME: closures

crates/hir_ty/src/infer.rs

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ use crate::{
4545
to_assoc_type_id, to_chalk_trait_id, AliasEq, AliasTy, Interner, TyKind,
4646
};
4747

48+
// This lint has a false positive here. See the link below for details.
49+
//
50+
// https://github.com/rust-lang/rust/issues/57411
51+
#[allow(unreachable_pub)]
52+
pub use unify::could_unify;
4853
pub(crate) use unify::unify;
4954

5055
mod unify;

crates/hir_ty/src/infer/unify.rs

+4
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ impl<T> Canonicalized<T> {
157157
}
158158
}
159159

160+
pub fn could_unify(t1: &Ty, t2: &Ty) -> bool {
161+
InferenceTable::new().unify(t1, t2)
162+
}
163+
160164
pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> {
161165
let mut table = InferenceTable::new();
162166
let vars = Substitution(

crates/hir_ty/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use crate::{
4141
};
4242

4343
pub use autoderef::autoderef;
44-
pub use infer::{InferenceResult, InferenceVar};
44+
pub use infer::{could_unify, InferenceResult, InferenceVar};
4545
pub use lower::{
4646
associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
4747
TyDefId, TyLoweringContext, ValueTyDefId,

crates/ide_completion/src/item.rs

+39-20
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl fmt::Debug for CompletionItem {
122122
}
123123
}
124124

125-
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
125+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
126126
pub struct CompletionRelevance {
127127
/// This is set in cases like these:
128128
///
@@ -134,31 +134,41 @@ pub struct CompletionRelevance {
134134
/// }
135135
/// ```
136136
pub exact_name_match: bool,
137+
/// See CompletionRelevanceTypeMatch doc comments for cases where this is set.
138+
pub type_match: Option<CompletionRelevanceTypeMatch>,
137139
/// This is set in cases like these:
138140
///
139141
/// ```
140-
/// fn f(spam: String) {}
141-
/// fn main {
142-
/// let foo = String::new();
143-
/// f($0) // type of local matches the type of param
142+
/// fn foo(a: u32) {
143+
/// let b = 0;
144+
/// $0 // `a` and `b` are local
144145
/// }
145146
/// ```
146-
pub exact_type_match: bool,
147+
pub is_local: bool,
148+
}
149+
150+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
151+
pub enum CompletionRelevanceTypeMatch {
147152
/// This is set in cases like these:
148153
///
149154
/// ```
150-
/// fn foo(bar: u32) {
151-
/// $0 // `bar` is local
155+
/// enum Option<T> { Some(T), None }
156+
/// fn f(a: Option<u32>) {}
157+
/// fn main {
158+
/// f(Option::N$0) // type `Option<T>` could unify with `Option<u32>`
152159
/// }
153160
/// ```
161+
CouldUnify,
162+
/// This is set in cases like these:
154163
///
155164
/// ```
156-
/// fn foo() {
157-
/// let bar = 0;
158-
/// $0 // `bar` is local
165+
/// fn f(spam: String) {}
166+
/// fn main {
167+
/// let foo = String::new();
168+
/// f($0) // type of local matches the type of param
159169
/// }
160170
/// ```
161-
pub is_local: bool,
171+
Exact,
162172
}
163173

164174
impl CompletionRelevance {
@@ -177,9 +187,11 @@ impl CompletionRelevance {
177187
if self.exact_name_match {
178188
score += 1;
179189
}
180-
if self.exact_type_match {
181-
score += 3;
182-
}
190+
score += match self.type_match {
191+
Some(CompletionRelevanceTypeMatch::Exact) => 4,
192+
Some(CompletionRelevanceTypeMatch::CouldUnify) => 3,
193+
None => 0,
194+
};
183195
if self.is_local {
184196
score += 1;
185197
}
@@ -342,7 +354,7 @@ impl CompletionItem {
342354
// match, but with exact type match set because self.ref_match
343355
// is only set if there is an exact type match.
344356
let mut relevance = self.relevance;
345-
relevance.exact_type_match = true;
357+
relevance.type_match = Some(CompletionRelevanceTypeMatch::Exact);
346358

347359
self.ref_match.map(|mutability| (mutability, relevance))
348360
}
@@ -523,7 +535,7 @@ mod tests {
523535
use itertools::Itertools;
524536
use test_utils::assert_eq_text;
525537

526-
use super::CompletionRelevance;
538+
use super::{CompletionRelevance, CompletionRelevanceTypeMatch};
527539

528540
/// Check that these are CompletionRelevance are sorted in ascending order
529541
/// by their relevance score.
@@ -576,15 +588,22 @@ mod tests {
576588
is_local: true,
577589
..CompletionRelevance::default()
578590
}],
579-
vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }],
591+
vec![CompletionRelevance {
592+
type_match: Some(CompletionRelevanceTypeMatch::CouldUnify),
593+
..CompletionRelevance::default()
594+
}],
595+
vec![CompletionRelevance {
596+
type_match: Some(CompletionRelevanceTypeMatch::Exact),
597+
..CompletionRelevance::default()
598+
}],
580599
vec![CompletionRelevance {
581600
exact_name_match: true,
582-
exact_type_match: true,
601+
type_match: Some(CompletionRelevanceTypeMatch::Exact),
583602
..CompletionRelevance::default()
584603
}],
585604
vec![CompletionRelevance {
586605
exact_name_match: true,
587-
exact_type_match: true,
606+
type_match: Some(CompletionRelevanceTypeMatch::Exact),
588607
is_local: true,
589608
}],
590609
];

crates/ide_completion/src/render.rs

+65-16
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use ide_db::{
2020
use syntax::TextRange;
2121

2222
use crate::{
23-
item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
24-
CompletionRelevance,
23+
item::{CompletionRelevanceTypeMatch, ImportEdit},
24+
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance,
2525
};
2626

2727
use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -143,7 +143,7 @@ impl<'a> Render<'a> {
143143
.set_deprecated(is_deprecated);
144144

145145
item.set_relevance(CompletionRelevance {
146-
exact_type_match: compute_exact_type_match(self.ctx.completion, ty),
146+
type_match: compute_type_match(self.ctx.completion, ty),
147147
exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()),
148148
..CompletionRelevance::default()
149149
});
@@ -245,7 +245,7 @@ impl<'a> Render<'a> {
245245
}
246246

247247
item.set_relevance(CompletionRelevance {
248-
exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
248+
type_match: compute_type_match(self.ctx.completion, &ty),
249249
exact_name_match: compute_exact_name_match(self.ctx.completion, &local_name),
250250
is_local: true,
251251
..CompletionRelevance::default()
@@ -309,14 +309,24 @@ impl<'a> Render<'a> {
309309
}
310310
}
311311

312-
fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> bool {
313-
match ctx.expected_type.as_ref() {
314-
Some(expected_type) => {
315-
// We don't ever consider unit type to be an exact type match, since
316-
// nearly always this is not meaningful to the user.
317-
completion_ty == expected_type && !expected_type.is_unit()
318-
}
319-
None => false,
312+
fn compute_type_match(
313+
ctx: &CompletionContext,
314+
completion_ty: &hir::Type,
315+
) -> Option<CompletionRelevanceTypeMatch> {
316+
let expected_type = ctx.expected_type.as_ref()?;
317+
318+
// We don't ever consider unit type to be an exact type match, since
319+
// nearly always this is not meaningful to the user.
320+
if expected_type.is_unit() {
321+
return None;
322+
}
323+
324+
if completion_ty == expected_type {
325+
Some(CompletionRelevanceTypeMatch::Exact)
326+
} else if expected_type.could_unify_with(completion_ty) {
327+
Some(CompletionRelevanceTypeMatch::CouldUnify)
328+
} else {
329+
None
320330
}
321331
}
322332

@@ -348,6 +358,7 @@ mod tests {
348358
use itertools::Itertools;
349359

350360
use crate::{
361+
item::CompletionRelevanceTypeMatch,
351362
test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
352363
CompletionKind, CompletionRelevance,
353364
};
@@ -360,7 +371,11 @@ mod tests {
360371
fn check_relevance(ra_fixture: &str, expect: Expect) {
361372
fn display_relevance(relevance: CompletionRelevance) -> String {
362373
let relevance_factors = vec![
363-
(relevance.exact_type_match, "type"),
374+
(relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),
375+
(
376+
relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify),
377+
"type_could_unify",
378+
),
364379
(relevance.exact_name_match, "name"),
365380
(relevance.is_local, "local"),
366381
]
@@ -533,7 +548,9 @@ fn main() { let _: m::Spam = S$0 }
533548
detail: "(i32)",
534549
relevance: CompletionRelevance {
535550
exact_name_match: false,
536-
exact_type_match: true,
551+
type_match: Some(
552+
Exact,
553+
),
537554
is_local: false,
538555
},
539556
trigger_call_info: true,
@@ -559,7 +576,9 @@ fn main() { let _: m::Spam = S$0 }
559576
detail: "()",
560577
relevance: CompletionRelevance {
561578
exact_name_match: false,
562-
exact_type_match: true,
579+
type_match: Some(
580+
Exact,
581+
),
563582
is_local: false,
564583
},
565584
},
@@ -1108,7 +1127,7 @@ fn main() {
11081127
detail: "S",
11091128
relevance: CompletionRelevance {
11101129
exact_name_match: true,
1111-
exact_type_match: false,
1130+
type_match: None,
11121131
is_local: true,
11131132
},
11141133
ref_match: "&mut ",
@@ -1353,4 +1372,34 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
13531372
"#]],
13541373
);
13551374
}
1375+
1376+
#[test]
1377+
fn generic_enum() {
1378+
check_relevance(
1379+
r#"
1380+
enum Foo<T> { A(T), B }
1381+
// bar() should not be an exact type match
1382+
// because the generic parameters are different
1383+
fn bar() -> Foo<u8> { Foo::B }
1384+
// FIXME baz() should be an exact type match
1385+
// because the types could unify, but it currently
1386+
// is not. This is due to the T here being
1387+
// TyKind::Placeholder rather than TyKind::Missing.
1388+
fn baz<T>() -> Foo<T> { Foo::B }
1389+
fn foo() {
1390+
let foo: Foo<u32> = Foo::B;
1391+
let _: Foo<u32> = f$0;
1392+
}
1393+
"#,
1394+
expect![[r#"
1395+
ev Foo::A(…) [type_could_unify]
1396+
ev Foo::B [type_could_unify]
1397+
lc foo [type+local]
1398+
en Foo []
1399+
fn baz() []
1400+
fn bar() []
1401+
fn foo() []
1402+
"#]],
1403+
);
1404+
}
13561405
}

crates/ide_completion/src/render/enum_variant.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use itertools::Itertools;
66

77
use crate::{
88
item::{CompletionItem, CompletionKind, ImportEdit},
9-
render::{builder_ext::Params, compute_exact_type_match, compute_ref_match, RenderContext},
9+
render::{builder_ext::Params, compute_ref_match, compute_type_match, RenderContext},
1010
CompletionRelevance,
1111
};
1212

@@ -77,7 +77,7 @@ impl<'a> EnumRender<'a> {
7777

7878
let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db);
7979
item.set_relevance(CompletionRelevance {
80-
exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
80+
type_match: compute_type_match(self.ctx.completion, &ty),
8181
..CompletionRelevance::default()
8282
});
8383

crates/ide_completion/src/render/function.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use syntax::ast::Fn;
88
use crate::{
99
item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit},
1010
render::{
11-
builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match,
11+
builder_ext::Params, compute_exact_name_match, compute_ref_match, compute_type_match,
1212
RenderContext,
1313
},
1414
};
@@ -73,7 +73,7 @@ impl<'a> FunctionRender<'a> {
7373

7474
let ret_type = self.func.ret_type(self.ctx.db());
7575
item.set_relevance(CompletionRelevance {
76-
exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type),
76+
type_match: compute_type_match(self.ctx.completion, &ret_type),
7777
exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()),
7878
..CompletionRelevance::default()
7979
});

crates/rust-analyzer/src/to_proto.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1138,7 +1138,7 @@ mod tests {
11381138
(
11391139
"&arg",
11401140
Some(
1141-
"fffffffa",
1141+
"fffffff9",
11421142
),
11431143
),
11441144
(

0 commit comments

Comments
 (0)