diff --git a/crates/hir/src/term_search/mod.rs b/crates/hir/src/term_search/mod.rs index 009d561e7d18..457165296ad7 100644 --- a/crates/hir/src/term_search/mod.rs +++ b/crates/hir/src/term_search/mod.rs @@ -152,11 +152,37 @@ impl LookupTable { &self.exhausted_scopedefs } + /// Types queried but not found fn take_types_wishlist(&mut self) -> FxHashSet { std::mem::take(&mut self.types_wishlist) } } +/// Context for the `term_search` function +pub struct TermSearchCtx<'a, DB: HirDatabase> { + /// Semantics for the program + pub sema: &'a Semantics<'a, DB>, + /// Semantic scope, captures context for the term search + pub scope: &'a SemanticsScope<'a>, + /// Target / expected output type + pub goal: Type, + /// Configuration for term search + pub config: TermSearchConfig, +} + +/// Configuration options for the term search +#[derive(Debug, Clone, Copy)] +pub struct TermSearchConfig { + /// Enable borrow checking, this guarantees the outputs of the `term_search` to borrow-check + pub enable_borrowcheck: bool, +} + +impl Default for TermSearchConfig { + fn default() -> Self { + Self { enable_borrowcheck: true } + } +} + /// # Term search /// /// Search for terms (expressions) that unify with the `goal` type. @@ -181,37 +207,32 @@ impl LookupTable { /// Note that there are usually more ways we can get to the `goal` type but some are discarded to /// reduce the memory consumption. It is also unlikely anyone is willing ti browse through /// thousands of possible responses so we currently take first 10 from every tactic. -pub fn term_search( - sema: &Semantics<'_, DB>, - scope: &SemanticsScope<'_>, - goal: &Type, -) -> Vec { +pub fn term_search(ctx: TermSearchCtx<'_, DB>) -> Vec { + let module = ctx.scope.module(); let mut defs = FxHashSet::default(); - defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(scope.module()))); + defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module))); - scope.process_all_names(&mut |_, def| { + ctx.scope.process_all_names(&mut |_, def| { defs.insert(def); }); - let module = scope.module(); let mut lookup = LookupTable::new(); // Try trivial tactic first, also populates lookup table - let mut solutions: Vec = - tactics::trivial(sema.db, &defs, &mut lookup, goal).collect(); + let mut solutions: Vec = 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(sema.db, &module, &defs, &mut lookup, goal)); + solutions.extend(tactics::famous_types(&ctx, &defs, &mut lookup)); let mut solution_found = !solutions.is_empty(); for _ in 0..5 { lookup.new_round(); - solutions.extend(tactics::type_constructor(sema.db, &module, &defs, &mut lookup, goal)); - solutions.extend(tactics::free_function(sema.db, &module, &defs, &mut lookup, goal)); - solutions.extend(tactics::impl_method(sema.db, &module, &defs, &mut lookup, goal)); - solutions.extend(tactics::struct_projection(sema.db, &module, &defs, &mut lookup, goal)); - solutions.extend(tactics::impl_static_method(sema.db, &module, &defs, &mut lookup, goal)); + 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)); // Break after 1 round after successful solution if solution_found { diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index 2a9d0d84518a..da0ffd59def7 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -1,11 +1,9 @@ //! Tactics for term search //! //! All the tactics take following arguments -//! * `db` - HIR database -//! * `module` - Module where the term search target location +//! * `ctx` - Context for the term search //! * `defs` - Set of items in scope at term search target location //! * `lookup` - Lookup table for types -//! * `goal` - Term search target type //! And they return iterator that yields type trees that unify with the `goal` type. use std::iter; @@ -17,13 +15,13 @@ use itertools::Itertools; use rustc_hash::FxHashSet; use crate::{ - Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, Module, ModuleDef, - ScopeDef, Type, Variant, + Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type, + Variant, }; -use crate::term_search::TypeTree; +use crate::term_search::{TermSearchConfig, TypeTree}; -use super::{LookupTable, NewTypesKey, MAX_VARIATIONS}; +use super::{LookupTable, NewTypesKey, TermSearchCtx, MAX_VARIATIONS}; /// # Trivial tactic /// @@ -31,41 +29,42 @@ use super::{LookupTable, NewTypesKey, MAX_VARIATIONS}; /// Also works as a starting point to move all items in scope to lookup table. /// /// # Arguments -/// * `db` - HIR database +/// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types -/// * `goal` - Term search target type /// /// Returns iterator that yields elements that unify with `goal`. /// /// _Note that there is no use of calling this tactic in every iteration as the output does not /// depend on the current state of `lookup`_ -pub(super) fn trivial<'a>( - db: &'a dyn HirDatabase, +pub(super) fn trivial<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet, lookup: &'a mut LookupTable, - goal: &'a Type, ) -> impl Iterator + 'a { + let db = ctx.sema.db; defs.iter().filter_map(|def| { let tt = match def { ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(TypeTree::Const(*it)), ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(TypeTree::Static(*it)), ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(TypeTree::ConstParam(*it)), ScopeDef::Local(it) => { - let borrowck = db.borrowck(it.parent).ok()?; + if ctx.config.enable_borrowcheck { + let borrowck = db.borrowck(it.parent).ok()?; - let invalid = borrowck.iter().any(|b| { - b.partially_moved.iter().any(|moved| { - Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id) - }) || b.borrow_regions.iter().any(|region| { - // Shared borrows are fine - Some(®ion.local) == b.mir_body.binding_locals.get(it.binding_id) - && region.kind != BorrowKind::Shared - }) - }); + let invalid = borrowck.iter().any(|b| { + b.partially_moved.iter().any(|moved| { + Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id) + }) || b.borrow_regions.iter().any(|region| { + // Shared borrows are fine + Some(®ion.local) == b.mir_body.binding_locals.get(it.binding_id) + && region.kind != BorrowKind::Shared + }) + }); - if invalid { - return None; + if invalid { + return None; + } } Some(TypeTree::Local(*it)) @@ -83,7 +82,7 @@ pub(super) fn trivial<'a>( return None; } - ty.could_unify_with_deeply(db, goal).then(|| tt) + ty.could_unify_with_deeply(db, &ctx.goal).then(|| tt) }) } @@ -95,24 +94,23 @@ pub(super) fn trivial<'a>( /// elements that unify with `goal`. /// /// # Arguments -/// * `db` - HIR database -/// * `module` - Module where the term search target location +/// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types -/// * `goal` - Term search target type -pub(super) fn type_constructor<'a>( - db: &'a dyn HirDatabase, - module: &'a Module, +pub(super) fn type_constructor<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet, lookup: &'a mut LookupTable, - goal: &'a Type, ) -> impl Iterator + 'a { + let db = ctx.sema.db; + let module = ctx.scope.module(); fn variant_helper( db: &dyn HirDatabase, lookup: &mut LookupTable, parent_enum: Enum, variant: Variant, goal: &Type, + config: &TermSearchConfig, ) -> Vec<(Type, Vec)> { let generics = GenericDef::from(variant.parent_enum(db)); @@ -151,7 +149,7 @@ pub(super) fn type_constructor<'a>( .permutations(non_default_type_params_len); generic_params - .filter_map(|generics| { + .filter_map(move |generics| { // Insert default type params let mut g = generics.into_iter(); let generics: Vec<_> = type_params @@ -171,7 +169,7 @@ pub(super) fn type_constructor<'a>( } // Ignore types that have something to do with lifetimes - if enum_ty.contains_reference(db) { + if config.enable_borrowcheck && enum_ty.contains_reference(db) { return None; } @@ -211,9 +209,10 @@ pub(super) fn type_constructor<'a>( .collect() } defs.iter() - .filter_map(|def| match def { + .filter_map(move |def| match def { ScopeDef::ModuleDef(ModuleDef::Variant(it)) => { - let variant_trees = variant_helper(db, lookup, it.parent_enum(db), *it, goal); + let variant_trees = + variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.goal, &ctx.config); if variant_trees.is_empty() { return None; } @@ -224,7 +223,9 @@ pub(super) fn type_constructor<'a>( let trees: Vec<(Type, Vec)> = enum_ .variants(db) .into_iter() - .flat_map(|it| variant_helper(db, lookup, enum_.clone(), it, goal)) + .flat_map(|it| { + variant_helper(db, lookup, enum_.clone(), it, &ctx.goal, &ctx.config) + }) .collect(); if !trees.is_empty() { @@ -234,8 +235,8 @@ pub(super) fn type_constructor<'a>( Some(trees) } ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => { - // Ignore unstable - if it.is_unstable(db) { + // Ignore unstable and not visible + if it.is_unstable(db) || !it.is_visible_from(db, module) { return None; } @@ -285,18 +286,18 @@ pub(super) fn type_constructor<'a>( // Allow types with generics only if they take us straight to goal for // performance reasons if non_default_type_params_len != 0 - && struct_ty.could_unify_with_deeply(db, goal) + && struct_ty.could_unify_with_deeply(db, &ctx.goal) { return None; } // Ignore types that have something to do with lifetimes - if struct_ty.contains_reference(db) { + if ctx.config.enable_borrowcheck && struct_ty.contains_reference(db) { return None; } let fileds = it.fields(db); // Check if all fields are visible, otherwise we cannot fill them - if fileds.iter().any(|it| !it.is_visible_from(db, *module)) { + if fileds.iter().any(|it| !it.is_visible_from(db, module)) { return None; } @@ -335,7 +336,7 @@ pub(super) fn type_constructor<'a>( _ => None, }) .flatten() - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees)) + .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) .flatten() } @@ -348,20 +349,18 @@ pub(super) fn type_constructor<'a>( /// elements that unify with `goal`. /// /// # Arguments -/// * `db` - HIR database -/// * `module` - Module where the term search target location +/// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types -/// * `goal` - Term search target type -pub(super) fn free_function<'a>( - db: &'a dyn HirDatabase, - module: &'a Module, +pub(super) fn free_function<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet, lookup: &'a mut LookupTable, - goal: &'a Type, ) -> impl Iterator + 'a { + let db = ctx.sema.db; + let module = ctx.scope.module(); defs.iter() - .filter_map(|def| match def { + .filter_map(move |def| match def { ScopeDef::ModuleDef(ModuleDef::Function(it)) => { let generics = GenericDef::from(*it); @@ -411,10 +410,10 @@ pub(super) fn free_function<'a>( let ret_ty = it.ret_type_with_generics(db, generics.iter().cloned()); // Filter out private and unsafe functions - if !it.is_visible_from(db, *module) + if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) - || ret_ty.contains_reference(db) + || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { return None; @@ -461,7 +460,7 @@ pub(super) fn free_function<'a>( _ => None, }) .flatten() - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees)) + .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) .flatten() } @@ -476,18 +475,16 @@ pub(super) fn free_function<'a>( /// elements that unify with `goal`. /// /// # Arguments -/// * `db` - HIR database -/// * `module` - Module where the term search target location +/// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types -/// * `goal` - Term search target type -pub(super) fn impl_method<'a>( - db: &'a dyn HirDatabase, - module: &'a Module, +pub(super) fn impl_method<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, - goal: &'a Type, ) -> impl Iterator + 'a { + let db = ctx.sema.db; + let module = ctx.scope.module(); lookup .new_types(NewTypesKey::ImplMethod) .into_iter() @@ -499,7 +496,7 @@ pub(super) fn impl_method<'a>( AssocItem::Function(f) => Some((imp, ty, f)), _ => None, }) - .filter_map(|(imp, ty, it)| { + .filter_map(move |(imp, ty, it)| { let fn_generics = GenericDef::from(it); let imp_generics = GenericDef::from(imp); @@ -520,7 +517,7 @@ pub(super) fn impl_method<'a>( } // Filter out private and unsafe functions - if !it.is_visible_from(db, *module) || it.is_unsafe_to_call(db) || it.is_unstable(db) { + if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) { return None; } @@ -570,7 +567,9 @@ pub(super) fn impl_method<'a>( ty.type_arguments().chain(generics.iter().cloned()), ); // Filter out functions that return references - if ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { + if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) + || ret_ty.is_raw_ptr() + { return None; } @@ -615,7 +614,7 @@ pub(super) fn impl_method<'a>( Some(trees) }) .flatten() - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees)) + .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) .flatten() } @@ -627,24 +626,21 @@ pub(super) fn impl_method<'a>( /// elements that unify with `goal`. /// /// # Arguments -/// * `db` - HIR database -/// * `module` - Module where the term search target location +/// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types -/// * `goal` - Term search target type -pub(super) fn struct_projection<'a>( - db: &'a dyn HirDatabase, - module: &'a Module, +pub(super) fn struct_projection<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, - goal: &'a Type, ) -> impl Iterator + 'a { + let db = ctx.sema.db; + let module = ctx.scope.module(); lookup .new_types(NewTypesKey::StructProjection) .into_iter() .map(|ty| (ty.clone(), lookup.find(db, &ty).expect("TypeTree not in lookup"))) .flat_map(move |(ty, targets)| { - let module = module.clone(); ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| { if !field.is_visible_from(db, module) { return None; @@ -656,7 +652,7 @@ pub(super) fn struct_projection<'a>( Some((filed_ty, trees)) }) }) - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees)) + .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) .flatten() } @@ -670,18 +666,16 @@ pub(super) fn struct_projection<'a>( /// _Note that there is no point of calling it iteratively as the output is always the same_ /// /// # Arguments -/// * `db` - HIR database -/// * `module` - Module where the term search target location +/// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types -/// * `goal` - Term search target type -pub(super) fn famous_types<'a>( - db: &'a dyn HirDatabase, - module: &'a Module, +pub(super) fn famous_types<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, - goal: &'a Type, ) -> impl Iterator + 'a { + let db = ctx.sema.db; + let module = ctx.scope.module(); [ TypeTree::FamousType { ty: Type::new(db, module.id, TyBuilder::bool()), value: "true" }, TypeTree::FamousType { ty: Type::new(db, module.id, TyBuilder::bool()), value: "false" }, @@ -692,7 +686,7 @@ pub(super) fn famous_types<'a>( lookup.insert(tt.ty(db), std::iter::once(tt.clone())); tt }) - .filter(|tt| tt.ty(db).could_unify_with_deeply(db, goal)) + .filter(|tt| tt.ty(db).could_unify_with_deeply(db, &ctx.goal)) } /// # Impl static method (without self type) tactic @@ -703,22 +697,20 @@ pub(super) fn famous_types<'a>( /// elements that unify with `goal`. /// /// # Arguments -/// * `db` - HIR database -/// * `module` - Module where the term search target location +/// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types -/// * `goal` - Term search target type -pub(super) fn impl_static_method<'a>( - db: &'a dyn HirDatabase, - module: &'a Module, +pub(super) fn impl_static_method<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, - goal: &'a Type, ) -> impl Iterator + 'a { + let db = ctx.sema.db; + let module = ctx.scope.module(); lookup .take_types_wishlist() .into_iter() - .chain(iter::once(goal.clone())) + .chain(iter::once(ctx.goal.clone())) .flat_map(|ty| { Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp)) }) @@ -728,7 +720,7 @@ pub(super) fn impl_static_method<'a>( AssocItem::Function(f) => Some((imp, ty, f)), _ => None, }) - .filter_map(|(imp, ty, it)| { + .filter_map(move |(imp, ty, it)| { let fn_generics = GenericDef::from(it); let imp_generics = GenericDef::from(imp); @@ -751,7 +743,7 @@ pub(super) fn impl_static_method<'a>( } // Filter out private and unsafe functions - if !it.is_visible_from(db, *module) || it.is_unsafe_to_call(db) || it.is_unstable(db) { + if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) { return None; } @@ -801,7 +793,9 @@ pub(super) fn impl_static_method<'a>( ty.type_arguments().chain(generics.iter().cloned()), ); // Filter out functions that return references - if ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { + if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) + || ret_ty.is_raw_ptr() + { return None; } @@ -845,6 +839,6 @@ pub(super) fn impl_static_method<'a>( Some(trees) }) .flatten() - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees)) + .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) .flatten() } diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index a32e36b71277..85d7add4a0b5 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs @@ -1,4 +1,5 @@ //! Term search assist +use hir::term_search::TermSearchCtx; use ide_db::assists::{AssistId, AssistKind, GroupLabel}; use itertools::Itertools; @@ -23,7 +24,13 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let scope = ctx.sema.scope(&parent)?; - let paths = hir::term_search::term_search(&ctx.sema, &scope, &target_ty); + let term_search_ctx = TermSearchCtx { + sema: &ctx.sema, + scope: &scope, + goal: target_ty, + config: Default::default(), + }; + let paths = hir::term_search::term_search(term_search_ctx); if paths.is_empty() { return None; diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index ba3c0cf3fd60..920db07e06f9 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -41,6 +41,7 @@ use crate::{ macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, render_field, render_path_resolution, render_pattern_resolution, render_tuple_field, + render_type_tree, type_alias::{render_type_alias, render_type_alias_with_eq}, union_literal::render_union_literal, RenderContext, @@ -157,6 +158,16 @@ impl Completions { item.add_to(self, ctx.db); } + pub(crate) fn add_expr( + &mut self, + ctx: &CompletionContext<'_>, + expr: &hir::term_search::TypeTree, + path_ctx: &PathCompletionCtx, + ) { + let item = render_type_tree(ctx, expr, path_ctx); + item.add_to(self, ctx.db); + } + pub(crate) fn add_crate_roots( &mut self, ctx: &CompletionContext<'_>, @@ -690,6 +701,7 @@ pub(super) fn complete_name_ref( match kind { NameRefKind::Path(path_ctx) => { flyimport::import_on_the_fly_path(acc, ctx, path_ctx); + expr::complete_expr(acc, ctx, path_ctx); match &path_ctx.kind { PathKind::Expr { expr_ctx } => { diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 77fd5dd98b8d..b8ed429cb24e 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -328,3 +328,32 @@ pub(crate) fn complete_expr_path( } } } + +pub(crate) fn complete_expr( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + path_ctx: &PathCompletionCtx, +) { + let _p = profile::span("complete_expr"); + if !ctx.qualifier_ctx.none() { + return; + } + + if let Some(ty) = &ctx.expected_type { + // Ignore unit types as they are not very interesting + if ty.is_unit() { + return; + } + + let term_search_ctx = hir::term_search::TermSearchCtx { + sema: &ctx.sema, + scope: &ctx.scope, + goal: ty.clone(), + config: hir::term_search::TermSearchConfig { enable_borrowcheck: false }, + }; + let exprs = hir::term_search::term_search(term_search_ctx); + for expr in exprs { + acc.add_expr(ctx, &expr, path_ctx); + } + } +} diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 2ed080a83479..6d91b379162d 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -272,6 +272,26 @@ pub(crate) fn render_resolution_with_import_pat( Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution)) } +pub(crate) fn render_type_tree( + ctx: &CompletionContext<'_>, + expr: &hir::term_search::TypeTree, + path_ctx: &PathCompletionCtx, +) -> Builder { + let mut item = CompletionItem::new( + CompletionItemKind::Snippet, + ctx.source_range(), + expr.gen_source_code(&ctx.scope), + ); + item.set_relevance(crate::CompletionRelevance { + type_match: Some(crate::item::CompletionRelevanceTypeMatch::CouldUnify), + ..Default::default() + }); + + path_ref_match(ctx, path_ctx, &expr.ty(ctx.sema.db), &mut item); + + item +} + fn scope_def_to_name( resolution: ScopeDef, ctx: &RenderContext<'_>, diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index ff585f3d15b8..62e20f48b801 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -1,4 +1,8 @@ -use hir::{db::ExpandDatabase, term_search::term_search, ClosureStyle, HirDisplay, Semantics}; +use hir::{ + db::ExpandDatabase, + term_search::{term_search, TermSearchCtx}, + ClosureStyle, HirDisplay, Semantics, +}; use ide_db::{ assists::{Assist, AssistId, AssistKind, GroupLabel}, label::Label, @@ -40,7 +44,8 @@ fn fixes(sema: &Semantics<'_, RootDatabase>, d: &hir::TypedHole) -> Option continue, }; - let found_terms = hir::term_search::term_search(&sema, &scope, &target_ty); + let ctx = hir::term_search::TermSearchCtx { + sema: &sema, + scope: &scope, + goal: target_ty, + config: hir::term_search::TermSearchConfig { + enable_borrowcheck: true, + }, + }; + let found_terms = hir::term_search::term_search(ctx); if found_terms.is_empty() { acc.tail_expr_no_term += 1;