Skip to content

Commit 1634be0

Browse files
committed
Add quantified trees to reduce autocomplete options
1 parent 296dadb commit 1634be0

File tree

16 files changed

+300
-94
lines changed

16 files changed

+300
-94
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ authors = ["rust-analyzer team"]
1212
[profile.dev]
1313
# Disabling debug info speeds up builds a bunch,
1414
# and we don't rely on it for debugging that much.
15-
debug = 0
15+
debug = 2
1616

1717
[profile.dev.package]
1818
# These speed up local tests.

crates/hir/src/term_search/mod.rs

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,59 @@ pub use type_tree::TypeTree;
1212

1313
mod tactics;
1414

15-
/// # Maximum amount of variations to take per type
16-
///
17-
/// This is to speed up term search as there may be huge amount of variations of arguments for
18-
/// function, even when the return type is always the same. The idea is to take first n and call it
19-
/// a day.
20-
const MAX_VARIATIONS: usize = 10;
21-
2215
/// Key for lookup table to query new types reached.
2316
#[derive(Debug, Hash, PartialEq, Eq)]
2417
enum NewTypesKey {
2518
ImplMethod,
2619
StructProjection,
2720
}
2821

22+
#[derive(Debug)]
23+
enum AlternativeTrees {
24+
Few(FxHashSet<TypeTree>),
25+
Many(Type),
26+
}
27+
28+
impl AlternativeTrees {
29+
pub fn new(
30+
threshold: usize,
31+
ty: Type,
32+
trees: impl Iterator<Item = TypeTree>,
33+
) -> AlternativeTrees {
34+
let mut it = AlternativeTrees::Few(Default::default());
35+
it.extend_with_threshold(threshold, ty, trees);
36+
it
37+
}
38+
39+
pub fn trees(&self) -> Vec<TypeTree> {
40+
match self {
41+
AlternativeTrees::Few(trees) => trees.iter().cloned().collect(),
42+
AlternativeTrees::Many(ty) => vec![TypeTree::Many(ty.clone())],
43+
}
44+
}
45+
46+
pub fn extend_with_threshold(
47+
&mut self,
48+
threshold: usize,
49+
ty: Type,
50+
mut trees: impl Iterator<Item = TypeTree>,
51+
) {
52+
match self {
53+
AlternativeTrees::Few(tts) => {
54+
while let Some(it) = trees.next() {
55+
if tts.len() > threshold {
56+
*self = AlternativeTrees::Many(ty);
57+
break;
58+
}
59+
60+
tts.insert(it);
61+
}
62+
}
63+
AlternativeTrees::Many(_) => (),
64+
}
65+
}
66+
}
67+
2968
/// # Lookup table for term search
3069
///
3170
/// Lookup table keeps all the state during term search.
@@ -38,7 +77,7 @@ enum NewTypesKey {
3877
#[derive(Default, Debug)]
3978
struct LookupTable {
4079
/// All the `TypeTree`s in "value" produce the type of "key"
41-
data: FxHashMap<Type, FxHashSet<TypeTree>>,
80+
data: FxHashMap<Type, AlternativeTrees>,
4281
/// New types reached since last query by the `NewTypesKey`
4382
new_types: FxHashMap<NewTypesKey, Vec<Type>>,
4483
/// ScopeDefs that are not interesting any more
@@ -49,6 +88,8 @@ struct LookupTable {
4988
rounds_since_sopedef_hit: FxHashMap<ScopeDef, u32>,
5089
/// Types queried but not present
5190
types_wishlist: FxHashSet<Type>,
91+
/// Threshold to squash trees to `Many`
92+
many_threshold: usize,
5293
}
5394

5495
impl LookupTable {
@@ -65,7 +106,7 @@ impl LookupTable {
65106
self.data
66107
.iter()
67108
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
68-
.map(|(_, tts)| tts.iter().cloned().collect())
109+
.map(|(_, tts)| tts.trees())
69110
}
70111

71112
/// Same as find but automatically creates shared reference of types in the lookup
@@ -76,15 +117,18 @@ impl LookupTable {
76117
self.data
77118
.iter()
78119
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
79-
.map(|(_, tts)| tts.iter().cloned().collect())
120+
.map(|(_, tts)| tts.trees())
80121
.or_else(|| {
81122
self.data
82123
.iter()
83124
.find(|(t, _)| {
84125
Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, &ty)
85126
})
86127
.map(|(_, tts)| {
87-
tts.iter().map(|tt| TypeTree::Reference(Box::new(tt.clone()))).collect()
128+
tts.trees()
129+
.into_iter()
130+
.map(|tt| TypeTree::Reference(Box::new(tt)))
131+
.collect()
88132
})
89133
})
90134
}
@@ -96,9 +140,12 @@ impl LookupTable {
96140
/// but they clearly do not unify themselves.
97141
fn insert(&mut self, ty: Type, trees: impl Iterator<Item = TypeTree>) {
98142
match self.data.get_mut(&ty) {
99-
Some(it) => it.extend(trees.take(MAX_VARIATIONS)),
143+
Some(it) => it.extend_with_threshold(self.many_threshold, ty, trees),
100144
None => {
101-
self.data.insert(ty.clone(), trees.take(MAX_VARIATIONS).collect());
145+
self.data.insert(
146+
ty.clone(),
147+
AlternativeTrees::new(self.many_threshold, ty.clone(), trees),
148+
);
102149
for it in self.new_types.values_mut() {
103150
it.push(ty.clone());
104151
}
@@ -175,11 +222,15 @@ pub struct TermSearchCtx<'a, DB: HirDatabase> {
175222
pub struct TermSearchConfig {
176223
/// Enable borrow checking, this guarantees the outputs of the `term_search` to borrow-check
177224
pub enable_borrowcheck: bool,
225+
/// Indicate when to squash multiple trees to `Many` as there are too many to keep track
226+
pub many_alternatives_threshold: usize,
227+
/// Depth of the search eg. number of cycles to run
228+
pub depth: usize,
178229
}
179230

180231
impl Default for TermSearchConfig {
181232
fn default() -> Self {
182-
Self { enable_borrowcheck: true }
233+
Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 5 }
183234
}
184235
}
185236

@@ -225,7 +276,7 @@ pub fn term_search<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree>
225276

226277
let mut solution_found = !solutions.is_empty();
227278

228-
for _ in 0..5 {
279+
for _ in 0..ctx.config.depth {
229280
lookup.new_round();
230281

231282
solutions.extend(tactics::type_constructor(&ctx, &defs, &mut lookup));

crates/hir/src/term_search/tactics.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::{
2121

2222
use crate::term_search::{TermSearchConfig, TypeTree};
2323

24-
use super::{LookupTable, NewTypesKey, TermSearchCtx, MAX_VARIATIONS};
24+
use super::{LookupTable, NewTypesKey, TermSearchCtx};
2525

2626
/// # Trivial tactic
2727
///
@@ -194,7 +194,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
194194
param_trees
195195
.into_iter()
196196
.multi_cartesian_product()
197-
.take(MAX_VARIATIONS)
198197
.map(|params| TypeTree::Variant {
199198
variant,
200199
generics: generics.clone(),
@@ -315,7 +314,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
315314
param_trees
316315
.into_iter()
317316
.multi_cartesian_product()
318-
.take(MAX_VARIATIONS)
319317
.map(|params| TypeTree::Struct {
320318
strukt: *it,
321319
generics: generics.clone(),
@@ -440,7 +438,6 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
440438
param_trees
441439
.into_iter()
442440
.multi_cartesian_product()
443-
.take(MAX_VARIATIONS)
444441
.map(|params| TypeTree::Function {
445442
func: *it,
446443
generics: generics.clone(),
@@ -603,7 +600,6 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
603600
let fn_trees: Vec<TypeTree> = std::iter::once(target_type_trees)
604601
.chain(param_trees.into_iter())
605602
.multi_cartesian_product()
606-
.take(MAX_VARIATIONS)
607603
.map(|params| TypeTree::Function { func: it, generics: Vec::new(), params })
608604
.collect();
609605

@@ -822,7 +818,6 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
822818
param_trees
823819
.into_iter()
824820
.multi_cartesian_product()
825-
.take(MAX_VARIATIONS)
826821
.map(|params| TypeTree::Function {
827822
func: it,
828823
generics: generics.clone(),

crates/hir/src/term_search/type_tree.rs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ pub enum TypeTree {
109109
Field { type_tree: Box<TypeTree>, field: Field },
110110
/// Passing type as reference (with `&`)
111111
Reference(Box<TypeTree>),
112+
/// Indicates possibility of many different options that all evaluate to `ty`
113+
Many(Type),
112114
}
113115

114116
impl TypeTree {
@@ -117,7 +119,11 @@ impl TypeTree {
117119
/// Note that trait imports are not added to generated code.
118120
/// To make sure that the code is valid, callee has to also ensure that all the traits listed
119121
/// by `traits_used` method are also imported.
120-
pub fn gen_source_code(&self, sema_scope: &SemanticsScope<'_>) -> String {
122+
pub fn gen_source_code(
123+
&self,
124+
sema_scope: &SemanticsScope<'_>,
125+
many_formatter: &mut dyn FnMut(&Type) -> String,
126+
) -> String {
121127
let db = sema_scope.db;
122128
match self {
123129
TypeTree::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
@@ -128,9 +134,15 @@ impl TypeTree {
128134
TypeTree::Function { func, params, .. } => {
129135
if let Some(self_param) = func.self_param(db) {
130136
let func_name = func.name(db).display(db.upcast()).to_string();
131-
let target = params.first().expect("no self param").gen_source_code(sema_scope);
132-
let args =
133-
params.iter().skip(1).map(|f| f.gen_source_code(sema_scope)).join(", ");
137+
let target = params
138+
.first()
139+
.expect("no self param")
140+
.gen_source_code(sema_scope, many_formatter);
141+
let args = params
142+
.iter()
143+
.skip(1)
144+
.map(|f| f.gen_source_code(sema_scope, many_formatter))
145+
.join(", ");
134146

135147
match func.as_assoc_item(db).unwrap().containing_trait_or_trait_impl(db) {
136148
Some(trait_) => {
@@ -149,7 +161,10 @@ impl TypeTree {
149161
None => format!("{target}.{func_name}({args})"),
150162
}
151163
} else {
152-
let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", ");
164+
let args = params
165+
.iter()
166+
.map(|f| f.gen_source_code(sema_scope, many_formatter))
167+
.join(", ");
153168

154169
match func.as_assoc_item(db).map(|it| it.container(db)) {
155170
Some(container) => {
@@ -194,7 +209,10 @@ impl TypeTree {
194209
};
195210
let inner = match variant.kind(db) {
196211
StructKind::Tuple => {
197-
let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", ");
212+
let args = params
213+
.iter()
214+
.map(|f| f.gen_source_code(sema_scope, many_formatter))
215+
.join(", ");
198216
format!("{generics_str}({args})")
199217
}
200218
StructKind::Record => {
@@ -206,7 +224,7 @@ impl TypeTree {
206224
format!(
207225
"{}: {}",
208226
f.name(db).display(db.upcast()).to_string(),
209-
a.gen_source_code(sema_scope)
227+
a.gen_source_code(sema_scope, many_formatter)
210228
)
211229
})
212230
.join(", ");
@@ -222,7 +240,10 @@ impl TypeTree {
222240
let generics = non_default_generics(db, (*strukt).into(), generics);
223241
let inner = match strukt.kind(db) {
224242
StructKind::Tuple => {
225-
let args = params.iter().map(|a| a.gen_source_code(sema_scope)).join(", ");
243+
let args = params
244+
.iter()
245+
.map(|a| a.gen_source_code(sema_scope, many_formatter))
246+
.join(", ");
226247
format!("({args})")
227248
}
228249
StructKind::Record => {
@@ -234,7 +255,7 @@ impl TypeTree {
234255
format!(
235256
"{}: {}",
236257
f.name(db).display(db.upcast()).to_string(),
237-
a.gen_source_code(sema_scope)
258+
a.gen_source_code(sema_scope, many_formatter)
238259
)
239260
})
240261
.join(", ");
@@ -254,14 +275,15 @@ impl TypeTree {
254275
format!("{prefix}{inner}")
255276
}
256277
TypeTree::Field { type_tree, field } => {
257-
let strukt = type_tree.gen_source_code(sema_scope);
278+
let strukt = type_tree.gen_source_code(sema_scope, many_formatter);
258279
let field = field.name(db).display(db.upcast()).to_string();
259280
format!("{strukt}.{field}")
260281
}
261282
TypeTree::Reference(type_tree) => {
262-
let inner = type_tree.gen_source_code(sema_scope);
283+
let inner = type_tree.gen_source_code(sema_scope, many_formatter);
263284
format!("&{inner}")
264285
}
286+
TypeTree::Many(ty) => many_formatter(ty),
265287
}
266288
}
267289

@@ -292,6 +314,7 @@ impl TypeTree {
292314
field.ty_with_generics(db, type_tree.ty(db).type_arguments())
293315
}
294316
TypeTree::Reference(it) => it.ty(db),
317+
TypeTree::Many(ty) => ty.clone(),
295318
}
296319
}
297320

crates/ide-assists/src/handlers/term_search.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
3636
return None;
3737
}
3838

39+
let mut formatter = |_: &hir::Type| String::from("todo!()");
3940
for path in paths.iter().unique() {
40-
let code = path.gen_source_code(&scope);
41+
let code = path.gen_source_code(&scope, &mut formatter);
4142
acc.add_group(
4243
&GroupLabel(String::from("Term search")),
4344
AssistId("term_search", AssistKind::Generate),

crates/ide-completion/src/completions.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,11 @@ impl Completions {
162162
&mut self,
163163
ctx: &CompletionContext<'_>,
164164
expr: &hir::term_search::TypeTree,
165-
path_ctx: &PathCompletionCtx,
166165
) {
167-
let item = render_type_tree(ctx, expr, path_ctx);
168-
item.add_to(self, ctx.db);
166+
match render_type_tree(ctx, expr) {
167+
Some(item) => item.add_to(self, ctx.db),
168+
None => (),
169+
}
169170
}
170171

171172
pub(crate) fn add_crate_roots(
@@ -698,10 +699,10 @@ pub(super) fn complete_name_ref(
698699
ctx: &CompletionContext<'_>,
699700
NameRefContext { nameref, kind }: &NameRefContext,
700701
) {
702+
expr::complete_expr(acc, ctx);
701703
match kind {
702704
NameRefKind::Path(path_ctx) => {
703705
flyimport::import_on_the_fly_path(acc, ctx, path_ctx);
704-
expr::complete_expr(acc, ctx, path_ctx);
705706

706707
match &path_ctx.kind {
707708
PathKind::Expr { expr_ctx } => {

crates/ide-completion/src/completions/expr.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,6 @@ pub(crate) fn complete_expr_path(
332332
pub(crate) fn complete_expr(
333333
acc: &mut Completions,
334334
ctx: &CompletionContext<'_>,
335-
path_ctx: &PathCompletionCtx,
336335
) {
337336
let _p = profile::span("complete_expr");
338337
if !ctx.qualifier_ctx.none() {
@@ -349,11 +348,15 @@ pub(crate) fn complete_expr(
349348
sema: &ctx.sema,
350349
scope: &ctx.scope,
351350
goal: ty.clone(),
352-
config: hir::term_search::TermSearchConfig { enable_borrowcheck: false },
351+
config: hir::term_search::TermSearchConfig {
352+
enable_borrowcheck: false,
353+
many_alternatives_threshold: 1,
354+
depth: 2,
355+
},
353356
};
354357
let exprs = hir::term_search::term_search(term_search_ctx);
355358
for expr in exprs {
356-
acc.add_expr(ctx, &expr, path_ctx);
359+
acc.add_expr(ctx, &expr);
357360
}
358361
}
359362
}

0 commit comments

Comments
 (0)