Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #17203 - kilpkonn:collapse_terms, r=Veykril
Fix OOM caused by term search The issue came from multi Cartesian product for exprs with many (25+) arguments, each having multiple options. The solution is two fold: ### Avoid blowing up in Cartesian product **Before the logic was:** 1. Find expressions for each argument/param - there may be many 2. Take the Cartesian product (which blows up in some cases) 4. If there are more than 2 options throw them away by squashing them to `Many` **Now the logic is:** 1. Find expressions for each argument/param and squash them to `Many` if there are more than 2 as otherwise we are guaranteed to also have more than 2 after taking the product which means squashing them anyway. 2. Take the Cartesian product on iterator 3. Start consuming it one by one 4. If there are more than 2 options throw them away by squashing them to `Many` (same as before) This is also why I had to update some tests as the expressions get squashed to many more eagerly. ### Use fuel to avoid long search times and high memory usage Now all the tactics use `should_continue: Fn() -> bool` to chech if they should keep iterating _(Similarly to chalk)_. This reduces the search times by a magnitude, for example from ~139ms/hole to ~14ms/hole for `ripgrep` crate. There are slightly less expressions found, but I think speed gain worth it for usability. Also note that syntactic hits decreases more because of squashing so you simple need to run search multiple times to get full terms. Also the worst case time (For example `nalgebra` crate cus it has tons of generics) has search times mostly under 200ms. Benchmarks on `ripgrep` crate Before: ``` Tail Expr syntactic hits: 291/1692 (17%) Tail Exprs found: 1253/1692 (74%) Term search avg time: 139ms ```` After: ``` Tail Expr syntactic hits: 239/1692 (14%) Tail Exprs found: 1226/1692 (72%) Term search avg time: 14ms ```
bors 2024-05-13
parent bf53c47 · parent ab18604 · commit 9db1258
-rw-r--r--crates/hir/src/term_search.rs47
-rw-r--r--crates/hir/src/term_search/expr.rs30
-rw-r--r--crates/hir/src/term_search/tactics.rs37
-rw-r--r--crates/ide-assists/src/assist_config.rs1
-rw-r--r--crates/ide-assists/src/handlers/term_search.rs4
-rw-r--r--crates/ide-assists/src/tests.rs3
-rw-r--r--crates/ide-completion/src/completions/expr.rs2
-rw-r--r--crates/ide-completion/src/config.rs1
-rw-r--r--crates/ide-completion/src/render.rs7
-rw-r--r--crates/ide-completion/src/tests.rs1
-rw-r--r--crates/ide-diagnostics/src/handlers/typed_hole.rs8
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs8
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs4
-rw-r--r--docs/user/generated_config.adoc10
-rw-r--r--editors/code/package.json12
17 files changed, 141 insertions, 37 deletions
diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs
index 93e7300491..5c5ddae19e 100644
--- a/crates/hir/src/term_search.rs
+++ b/crates/hir/src/term_search.rs
@@ -127,6 +127,13 @@ impl LookupTable {
self.types_wishlist.insert(ty.clone());
}
+ // Collapse suggestions if there are many
+ if let Some(res) = &res {
+ if res.len() > self.many_threshold {
+ return Some(vec![Expr::Many(ty.clone())]);
+ }
+ }
+
res
}
@@ -158,6 +165,13 @@ impl LookupTable {
self.types_wishlist.insert(ty.clone());
}
+ // Collapse suggestions if there are many
+ if let Some(res) = &res {
+ if res.len() > self.many_threshold {
+ return Some(vec![Expr::Many(ty.clone())]);
+ }
+ }
+
res
}
@@ -255,13 +269,13 @@ pub struct TermSearchConfig {
pub enable_borrowcheck: bool,
/// Indicate when to squash multiple trees to `Many` as there are too many to keep track
pub many_alternatives_threshold: usize,
- /// Depth of the search eg. number of cycles to run
- pub depth: usize,
+ /// Fuel for term search in "units of work"
+ pub fuel: u64,
}
impl Default for TermSearchConfig {
fn default() -> Self {
- Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 6 }
+ Self { enable_borrowcheck: true, many_alternatives_threshold: 1, fuel: 400 }
}
}
@@ -280,8 +294,7 @@ impl Default for TermSearchConfig {
/// transformation tactics. For example functions take as from set of types (arguments) to some
/// type (return type). Other transformations include methods on type, type constructors and
/// projections to struct fields (field access).
-/// 3. Once we manage to find path to type we are interested in we continue for single round to see
-/// if we can find more paths that take us to the `goal` type.
+/// 3. If we run out of fuel (term search takes too long) we stop iterating.
/// 4. Return all the paths (type trees) that take us to the `goal` type.
///
/// Note that there are usually more ways we can get to the `goal` type but some are discarded to
@@ -297,21 +310,31 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
});
let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold, ctx.goal.clone());
+ let fuel = std::cell::Cell::new(ctx.config.fuel);
+
+ let should_continue = &|| {
+ let remaining = fuel.get();
+ fuel.set(remaining.saturating_sub(1));
+ if remaining == 0 {
+ tracing::debug!("fuel exhausted");
+ }
+ remaining > 0
+ };
// Try trivial tactic first, also populates lookup table
let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect();
// Use well known types tactic before iterations as it does not depend on other tactics
solutions.extend(tactics::famous_types(ctx, &defs, &mut lookup));
- for _ in 0..ctx.config.depth {
+ while should_continue() {
lookup.new_round();
- solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup));
- solutions.extend(tactics::free_function(ctx, &defs, &mut lookup));
- solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup));
- solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup));
- solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup));
- solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup));
+ solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup, should_continue));
+ solutions.extend(tactics::free_function(ctx, &defs, &mut lookup, should_continue));
+ solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup, should_continue));
+ solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup, should_continue));
+ solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup, should_continue));
+ solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup, should_continue));
// Discard not interesting `ScopeDef`s for speedup
for def in lookup.exhausted_scopedefs() {
diff --git a/crates/hir/src/term_search/expr.rs b/crates/hir/src/term_search/expr.rs
index 2d0c5630e1..9f56a1ee55 100644
--- a/crates/hir/src/term_search/expr.rs
+++ b/crates/hir/src/term_search/expr.rs
@@ -211,13 +211,13 @@ impl Expr {
}
}
Expr::Method { func, target, params, .. } => {
- if target.contains_many_in_illegal_pos() {
+ if self.contains_many_in_illegal_pos(db) {
return Ok(many_formatter(&target.ty(db)));
}
let func_name = func.name(db).display(db.upcast()).to_string();
let self_param = func.self_param(db).unwrap();
- let target = target.gen_source_code(
+ let target_str = target.gen_source_code(
sema_scope,
many_formatter,
prefer_no_std,
@@ -236,9 +236,12 @@ impl Expr {
Some(trait_) => {
let trait_name = mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))?;
let target = match self_param.access(db) {
- crate::Access::Shared => format!("&{target}"),
- crate::Access::Exclusive => format!("&mut {target}"),
- crate::Access::Owned => target,
+ crate::Access::Shared if !target.is_many() => format!("&{target_str}"),
+ crate::Access::Exclusive if !target.is_many() => {
+ format!("&mut {target_str}")
+ }
+ crate::Access::Owned => target_str,
+ _ => many_formatter(&target.ty(db)),
};
let res = match args.is_empty() {
true => format!("{trait_name}::{func_name}({target})",),
@@ -246,7 +249,7 @@ impl Expr {
};
Ok(res)
}
- None => Ok(format!("{target}.{func_name}({args})")),
+ None => Ok(format!("{target_str}.{func_name}({args})")),
}
}
Expr::Variant { variant, generics, params } => {
@@ -381,7 +384,7 @@ impl Expr {
Ok(res)
}
Expr::Field { expr, field } => {
- if expr.contains_many_in_illegal_pos() {
+ if expr.contains_many_in_illegal_pos(db) {
return Ok(many_formatter(&expr.ty(db)));
}
@@ -395,7 +398,7 @@ impl Expr {
Ok(format!("{strukt}.{field}"))
}
Expr::Reference(expr) => {
- if expr.contains_many_in_illegal_pos() {
+ if expr.contains_many_in_illegal_pos(db) {
return Ok(many_formatter(&expr.ty(db)));
}
@@ -466,10 +469,15 @@ impl Expr {
/// macro!().bar()
/// &macro!()
/// ```
- fn contains_many_in_illegal_pos(&self) -> bool {
+ fn contains_many_in_illegal_pos(&self, db: &dyn HirDatabase) -> bool {
match self {
- Expr::Method { target, .. } => target.contains_many_in_illegal_pos(),
- Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(),
+ Expr::Method { target, func, .. } => {
+ match func.as_assoc_item(db).and_then(|it| it.container_or_implemented_trait(db)) {
+ Some(_) => false,
+ None => target.is_many(),
+ }
+ }
+ Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(db),
Expr::Reference(target) => target.is_many(),
Expr::Many(_) => true,
_ => false,
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs
index 63b2a2506f..a26728272d 100644
--- a/crates/hir/src/term_search/tactics.rs
+++ b/crates/hir/src/term_search/tactics.rs
@@ -4,6 +4,7 @@
//! * `ctx` - Context for the term search
//! * `defs` - Set of items in scope at term search target location
//! * `lookup` - Lookup table for types
+//! * `should_continue` - Function that indicates when to stop iterating
//! And they return iterator that yields type trees that unify with the `goal` type.
use std::iter;
@@ -97,16 +98,19 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
+/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn type_constructor<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
+ should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
fn variant_helper(
db: &dyn HirDatabase,
lookup: &mut LookupTable,
+ should_continue: &dyn std::ops::Fn() -> bool,
parent_enum: Enum,
variant: Variant,
config: &TermSearchConfig,
@@ -152,6 +156,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
.chain((non_default_type_params_len == 0).then_some(Vec::new()));
generic_params
+ .filter(|_| should_continue())
.filter_map(move |generics| {
// Insert default type params
let mut g = generics.into_iter();
@@ -194,8 +199,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
defs.iter()
.filter_map(move |def| match def {
ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
- let variant_exprs =
- variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.config);
+ let variant_exprs = variant_helper(
+ db,
+ lookup,
+ should_continue,
+ it.parent_enum(db),
+ *it,
+ &ctx.config,
+ );
if variant_exprs.is_empty() {
return None;
}
@@ -213,7 +224,9 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
let exprs: Vec<(Type, Vec<Expr>)> = enum_
.variants(db)
.into_iter()
- .flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.config))
+ .flat_map(|it| {
+ variant_helper(db, lookup, should_continue, *enum_, it, &ctx.config)
+ })
.collect();
if exprs.is_empty() {
@@ -271,6 +284,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
.chain((non_default_type_params_len == 0).then_some(Vec::new()));
let exprs = generic_params
+ .filter(|_| should_continue())
.filter_map(|generics| {
// Insert default type params
let mut g = generics.into_iter();
@@ -345,10 +359,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
+/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn free_function<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
+ should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@@ -390,6 +406,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
.permutations(non_default_type_params_len);
let exprs: Vec<_> = generic_params
+ .filter(|_| should_continue())
.filter_map(|generics| {
// Insert default type params
let mut g = generics.into_iter();
@@ -474,10 +491,12 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
+/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn impl_method<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
+ should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@@ -554,6 +573,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
.permutations(non_default_fn_type_params_len);
let exprs: Vec<_> = generic_params
+ .filter(|_| should_continue())
.filter_map(|generics| {
// Insert default type params
let mut g = generics.into_iter();
@@ -645,10 +665,12 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
+/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn struct_projection<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
+ should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@@ -656,6 +678,7 @@ pub(super) fn struct_projection<'a, DB: HirDatabase>(
.new_types(NewTypesKey::StructProjection)
.into_iter()
.map(|ty| (ty.clone(), lookup.find(db, &ty).expect("Expr not in lookup")))
+ .filter(|_| should_continue())
.flat_map(move |(ty, targets)| {
ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| {
if !field.is_visible_from(db, module) {
@@ -716,10 +739,12 @@ pub(super) fn famous_types<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
+/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn impl_static_method<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
+ should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@@ -728,6 +753,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
.clone()
.into_iter()
.chain(iter::once(ctx.goal.clone()))
+ .filter(|_| should_continue())
.flat_map(|ty| {
Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
})
@@ -801,6 +827,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
.permutations(non_default_fn_type_params_len);
let exprs: Vec<_> = generic_params
+ .filter(|_| should_continue())
.filter_map(|generics| {
// Insert default type params
let mut g = generics.into_iter();
@@ -884,10 +911,12 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
+/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn make_tuple<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
+ should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@@ -896,6 +925,7 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>(
.types_wishlist()
.clone()
.into_iter()
+ .filter(|_| should_continue())
.filter(|ty| ty.is_tuple())
.filter_map(move |ty| {
// Double check to not contain unknown
@@ -915,6 +945,7 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>(
let exprs: Vec<Expr> = param_exprs
.into_iter()
.multi_cartesian_product()
+ .filter(|_| should_continue())
.map(|params| {
let tys: Vec<Type> = params.iter().map(|it| it.ty(db)).collect();
let tuple_ty = Type::new_tuple(module.krate().into(), &tys);
diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs
index fbe17dbfd7..5d76cb0432 100644
--- a/crates/ide-assists/src/assist_config.rs
+++ b/crates/ide-assists/src/assist_config.rs
@@ -16,4 +16,5 @@ pub struct AssistConfig {
pub prefer_no_std: bool,
pub prefer_prelude: bool,
pub assist_emit_must_use: bool,
+ pub term_search_fuel: u64,
}
diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs
index 0f4a8e3aec..d0c6ae2198 100644
--- a/crates/ide-assists/src/handlers/term_search.rs
+++ b/crates/ide-assists/src/handlers/term_search.rs
@@ -1,5 +1,5 @@
//! Term search assist
-use hir::term_search::TermSearchCtx;
+use hir::term_search::{TermSearchConfig, TermSearchCtx};
use ide_db::{
assists::{AssistId, AssistKind, GroupLabel},
famous_defs::FamousDefs,
@@ -34,7 +34,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
sema: &ctx.sema,
scope: &scope,
goal: target_ty,
- config: Default::default(),
+ config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() },
};
let paths = hir::term_search::term_search(&term_search_ctx);
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs
index 32d6984102..3b6c951251 100644
--- a/crates/ide-assists/src/tests.rs
+++ b/crates/ide-assists/src/tests.rs
@@ -31,6 +31,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
prefer_no_std: false,
prefer_prelude: true,
assist_emit_must_use: false,
+ term_search_fuel: 400,
};
pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
@@ -46,6 +47,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
prefer_no_std: false,
prefer_prelude: true,
assist_emit_must_use: false,
+ term_search_fuel: 400,
};
pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
@@ -61,6 +63,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
prefer_no_std: false,
prefer_prelude: true,
assist_emit_must_use: false,
+ term_search_fuel: 400,
};
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 802e9bc3a8..1e31d65fdd 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -353,7 +353,7 @@ pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>)
config: hir::term_search::TermSearchConfig {
enable_borrowcheck: false,
many_alternatives_threshold: 1,
- depth: 6,
+ fuel: 200,
},
};
let exprs = hir::term_search::term_search(&term_search_ctx);
diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs
index 04563fb0f4..809c305ed8 100644
--- a/crates/ide-completion/src/config.rs
+++ b/crates/ide-completion/src/config.rs
@@ -15,6 +15,7 @@ pub struct CompletionConfig {
pub enable_self_on_the_fly: bool,
pub enable_private_editable: bool,
pub enable_term_search: bool,
+ pub term_search_fuel: u64,
pub full_function_signatures: bool,
pub callable: Option<CallableSnippets>,
pub snippet_cap: Option<SnippetCap>,
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index db69b22078..7fa31e2757 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -1853,8 +1853,7 @@ fn f() { A { bar: b$0 }; }
fn baz() [type]
ex baz() [type]
ex bar() [type]
- ex A { bar: baz() }.bar [type]
- ex A { bar: bar() }.bar [type]
+ ex A { bar: ... }.bar [type]
st A []
fn f() []
"#]],
@@ -1992,8 +1991,8 @@ fn main() {
}
"#,
expect![[r#"
- ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify]
+ ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
lc m [local]
lc t [local]
lc &t [type+local]
@@ -2042,8 +2041,8 @@ fn main() {
}
"#,
expect![[r#"
- ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify]
ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify]
+ ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify]
lc m [local]
lc t [local]
lc &mut t [type+local]
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index 1f032c7df4..70e0aa4e9a 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -80,6 +80,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
},
snippets: Vec::new(),
limit: None,
+ term_search_fuel: 200,
};
pub(crate) fn completion_list(ra_fixture: &str) -> String {
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs
index 56c8181e84..656d79dc73 100644
--- a/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -1,6 +1,6 @@
use hir::{
db::ExpandDatabase,
- term_search::{term_search, TermSearchCtx},
+ term_search::{term_search, TermSearchConfig, TermSearchCtx},
ClosureStyle, HirDisplay,
};
use ide_db::{
@@ -47,7 +47,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
sema: &ctx.sema,
scope: &scope,
goal: d.expected.clone(),
- config: Default::default(),
+ config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() },
};
let paths = term_search(&term_search_ctx);
@@ -274,7 +274,7 @@ impl Foo for Baz {
}
fn asd() -> Bar {
let a = Baz;
- Foo::foo(a)
+ Foo::foo(_)
}
",
);
@@ -363,7 +363,7 @@ impl Foo for A {
}
fn main() {
let a = A;
- let c: Bar = Foo::foo(&a);
+ let c: Bar = Foo::foo(_);
}"#,
);
}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index c3ced36a69..135824386a 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -232,6 +232,7 @@ pub struct DiagnosticsConfig {
pub insert_use: InsertUseConfig,
pub prefer_no_std: bool,
pub prefer_prelude: bool,
+ pub term_search_fuel: u64,
}
impl DiagnosticsConfig {
@@ -256,6 +257,7 @@ impl DiagnosticsConfig {
},
prefer_no_std: false,
prefer_prelude: true,
+ term_search_fuel: 400,
}
}
}
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index a1eea8839e..5208aa9bf0 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -986,6 +986,7 @@ impl flags::AnalysisStats {
prefer_no_std: false,
prefer_prelude: true,
style_lints: false,
+ term_search_fuel: 400,
},
ide::AssistResolveStrategy::All,
file_id,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index e16595c992..6c332ae1cb 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -454,6 +454,9 @@ config_data! {
/// Local configurations can be overridden for every crate by placing a `rust-analyzer.toml` on crate root.
/// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle.
local: struct LocalDefaultConfigData <- LocalConfigInput -> {
+ /// Term search fuel in "units of work" for assists (Defaults to 400).
+ assist_termSearch_fuel: usize = 400,
+
/// Toggles the additional completions that automatically add imports when completed.
/// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
completion_autoimport_enable: bool = true,
@@ -515,6 +518,8 @@ config_data! {
}"#).unwrap(),
/// Whether to enable term search based snippets like `Some(foo.bar().baz())`.
completion_termSearch_enable: bool = false,
+ /// Term search fuel in "units of work" for autocompletion (Defaults to 200).
+ completion_termSearch_fuel: usize = 200,
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
highlightRelated_breakPoints_enable: bool = true,
@@ -1015,6 +1020,7 @@ impl Config {
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
assist_emit_must_use: self.assist_emitMustUse().to_owned(),
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
+ term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
}
}
@@ -1048,6 +1054,7 @@ impl Config {
snippets: self.snippets.clone().to_vec(),
limit: self.completion_limit(source_root).to_owned(),
enable_term_search: self.completion_termSearch_enable(source_root).to_owned(),
+ term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64,
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
}
}
@@ -1067,6 +1074,7 @@ impl Config {
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
style_lints: self.diagnostics_styleLints_enable().to_owned(),
+ term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
}
}
pub fn expand_proc_attr_macros(&self) -> bool {
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 7b385ca9d9..cc83d6246b 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -153,6 +153,7 @@ fn integrated_completion_benchmark() {
prefer_no_std: false,
prefer_prelude: true,
limit: None,
+ term_search_fuel: 200,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -197,6 +198,7 @@ fn integrated_completion_benchmark() {
prefer_no_std: false,
prefer_prelude: true,
limit: None,
+ term_search_fuel: 200,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -239,6 +241,7 @@ fn integrated_completion_benchmark() {
prefer_no_std: false,
prefer_prelude: true,
limit: None,
+ term_search_fuel: 200,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -295,6 +298,7 @@ fn integrated_diagnostics_benchmark() {
},
prefer_no_std: false,
prefer_prelude: false,
+ term_search_fuel: 400,
};
host.analysis()
.diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index e9d60063c6..8993a46d2b 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -9,6 +9,11 @@ for enum variants.
--
Placeholder expression to use for missing expressions in assists.
--
+[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `400`)::
++
+--
+Term search fuel in "units of work" for assists (Defaults to 400).
+--
[[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`)::
+
--
@@ -373,6 +378,11 @@ Custom completion snippets.
--
Whether to enable term search based snippets like `Some(foo.bar().baz())`.
--
+[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `200`)::
++
+--
+Term search fuel in "units of work" for autocompletion (Defaults to 200).
+--
[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`)::
+
--
diff --git a/editors/code/package.json b/editors/code/package.json
index c4bbb7932e..6e4fedd992 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -534,6 +534,12 @@
"Fill missing expressions with reasonable defaults, `new` or `default` constructors."
]
},
+ "rust-analyzer.assist.termSearch.fuel": {
+ "markdownDescription": "Term search fuel in \"units of work\" for assists (Defaults to 400).",
+ "default": 400,
+ "type": "integer",
+ "minimum": 0
+ },
"rust-analyzer.cachePriming.enable": {
"markdownDescription": "Warm up caches on project load.",
"default": true,
@@ -930,6 +936,12 @@
"default": false,
"type": "boolean"
},
+ "rust-analyzer.completion.termSearch.fuel": {
+ "markdownDescription": "Term search fuel in \"units of work\" for autocompletion (Defaults to 200).",
+ "default": 200,
+ "type": "integer",
+ "minimum": 0
+ },
"rust-analyzer.diagnostics.disabled": {
"markdownDescription": "List of rust-analyzer diagnostics to disable.",
"default": [],