mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
Initial version of term_search for autocomplete
This commit is contained in:
parent
627255dd5a
commit
bdbdd83ec1
8 changed files with 210 additions and 114 deletions
|
@ -152,11 +152,37 @@ impl LookupTable {
|
||||||
&self.exhausted_scopedefs
|
&self.exhausted_scopedefs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Types queried but not found
|
||||||
fn take_types_wishlist(&mut self) -> FxHashSet<Type> {
|
fn take_types_wishlist(&mut self) -> FxHashSet<Type> {
|
||||||
std::mem::take(&mut self.types_wishlist)
|
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
|
/// # Term search
|
||||||
///
|
///
|
||||||
/// Search for terms (expressions) that unify with the `goal` type.
|
/// 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
|
/// 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
|
/// 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.
|
/// thousands of possible responses so we currently take first 10 from every tactic.
|
||||||
pub fn term_search<DB: HirDatabase>(
|
pub fn term_search<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree> {
|
||||||
sema: &Semantics<'_, DB>,
|
let module = ctx.scope.module();
|
||||||
scope: &SemanticsScope<'_>,
|
|
||||||
goal: &Type,
|
|
||||||
) -> Vec<TypeTree> {
|
|
||||||
let mut defs = FxHashSet::default();
|
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);
|
defs.insert(def);
|
||||||
});
|
});
|
||||||
let module = scope.module();
|
|
||||||
|
|
||||||
let mut lookup = LookupTable::new();
|
let mut lookup = LookupTable::new();
|
||||||
|
|
||||||
// Try trivial tactic first, also populates lookup table
|
// Try trivial tactic first, also populates lookup table
|
||||||
let mut solutions: Vec<TypeTree> =
|
let mut solutions: Vec<TypeTree> = tactics::trivial(&ctx, &defs, &mut lookup).collect();
|
||||||
tactics::trivial(sema.db, &defs, &mut lookup, goal).collect();
|
|
||||||
// Use well known types tactic before iterations as it does not depend on other tactics
|
// 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();
|
let mut solution_found = !solutions.is_empty();
|
||||||
|
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
lookup.new_round();
|
lookup.new_round();
|
||||||
|
|
||||||
solutions.extend(tactics::type_constructor(sema.db, &module, &defs, &mut lookup, goal));
|
solutions.extend(tactics::type_constructor(&ctx, &defs, &mut lookup));
|
||||||
solutions.extend(tactics::free_function(sema.db, &module, &defs, &mut lookup, goal));
|
solutions.extend(tactics::free_function(&ctx, &defs, &mut lookup));
|
||||||
solutions.extend(tactics::impl_method(sema.db, &module, &defs, &mut lookup, goal));
|
solutions.extend(tactics::impl_method(&ctx, &defs, &mut lookup));
|
||||||
solutions.extend(tactics::struct_projection(sema.db, &module, &defs, &mut lookup, goal));
|
solutions.extend(tactics::struct_projection(&ctx, &defs, &mut lookup));
|
||||||
solutions.extend(tactics::impl_static_method(sema.db, &module, &defs, &mut lookup, goal));
|
solutions.extend(tactics::impl_static_method(&ctx, &defs, &mut lookup));
|
||||||
|
|
||||||
// Break after 1 round after successful solution
|
// Break after 1 round after successful solution
|
||||||
if solution_found {
|
if solution_found {
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
//! Tactics for term search
|
//! Tactics for term search
|
||||||
//!
|
//!
|
||||||
//! All the tactics take following arguments
|
//! All the tactics take following arguments
|
||||||
//! * `db` - HIR database
|
//! * `ctx` - Context for the term search
|
||||||
//! * `module` - Module where the term search target location
|
|
||||||
//! * `defs` - Set of items in scope at term search target location
|
//! * `defs` - Set of items in scope at term search target location
|
||||||
//! * `lookup` - Lookup table for types
|
//! * `lookup` - Lookup table for types
|
||||||
//! * `goal` - Term search target type
|
|
||||||
//! And they return iterator that yields type trees that unify with the `goal` type.
|
//! And they return iterator that yields type trees that unify with the `goal` type.
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
@ -17,13 +15,13 @@ use itertools::Itertools;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, Module, ModuleDef,
|
Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type,
|
||||||
ScopeDef, Type, Variant,
|
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
|
/// # 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.
|
/// Also works as a starting point to move all items in scope to lookup table.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `db` - HIR database
|
/// * `ctx` - Context for the term search
|
||||||
/// * `defs` - Set of items in scope at term search target location
|
/// * `defs` - Set of items in scope at term search target location
|
||||||
/// * `lookup` - Lookup table for types
|
/// * `lookup` - Lookup table for types
|
||||||
/// * `goal` - Term search target type
|
|
||||||
///
|
///
|
||||||
/// Returns iterator that yields elements that unify with `goal`.
|
/// 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
|
/// _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`_
|
/// depend on the current state of `lookup`_
|
||||||
pub(super) fn trivial<'a>(
|
pub(super) fn trivial<'a, DB: HirDatabase>(
|
||||||
db: &'a dyn HirDatabase,
|
ctx: &'a TermSearchCtx<'a, DB>,
|
||||||
defs: &'a FxHashSet<ScopeDef>,
|
defs: &'a FxHashSet<ScopeDef>,
|
||||||
lookup: &'a mut LookupTable,
|
lookup: &'a mut LookupTable,
|
||||||
goal: &'a Type,
|
|
||||||
) -> impl Iterator<Item = TypeTree> + 'a {
|
) -> impl Iterator<Item = TypeTree> + 'a {
|
||||||
|
let db = ctx.sema.db;
|
||||||
defs.iter().filter_map(|def| {
|
defs.iter().filter_map(|def| {
|
||||||
let tt = match def {
|
let tt = match def {
|
||||||
ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(TypeTree::Const(*it)),
|
ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(TypeTree::Const(*it)),
|
||||||
ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(TypeTree::Static(*it)),
|
ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(TypeTree::Static(*it)),
|
||||||
ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(TypeTree::ConstParam(*it)),
|
ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(TypeTree::ConstParam(*it)),
|
||||||
ScopeDef::Local(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| {
|
let invalid = borrowck.iter().any(|b| {
|
||||||
b.partially_moved.iter().any(|moved| {
|
b.partially_moved.iter().any(|moved| {
|
||||||
Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id)
|
Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id)
|
||||||
}) || b.borrow_regions.iter().any(|region| {
|
}) || b.borrow_regions.iter().any(|region| {
|
||||||
// Shared borrows are fine
|
// Shared borrows are fine
|
||||||
Some(®ion.local) == b.mir_body.binding_locals.get(it.binding_id)
|
Some(®ion.local) == b.mir_body.binding_locals.get(it.binding_id)
|
||||||
&& region.kind != BorrowKind::Shared
|
&& region.kind != BorrowKind::Shared
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if invalid {
|
if invalid {
|
||||||
return None;
|
return None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(TypeTree::Local(*it))
|
Some(TypeTree::Local(*it))
|
||||||
|
@ -83,7 +82,7 @@ pub(super) fn trivial<'a>(
|
||||||
return None;
|
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`.
|
/// elements that unify with `goal`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `db` - HIR database
|
/// * `ctx` - Context for the term search
|
||||||
/// * `module` - Module where the term search target location
|
|
||||||
/// * `defs` - Set of items in scope at term search target location
|
/// * `defs` - Set of items in scope at term search target location
|
||||||
/// * `lookup` - Lookup table for types
|
/// * `lookup` - Lookup table for types
|
||||||
/// * `goal` - Term search target type
|
pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
||||||
pub(super) fn type_constructor<'a>(
|
ctx: &'a TermSearchCtx<'a, DB>,
|
||||||
db: &'a dyn HirDatabase,
|
|
||||||
module: &'a Module,
|
|
||||||
defs: &'a FxHashSet<ScopeDef>,
|
defs: &'a FxHashSet<ScopeDef>,
|
||||||
lookup: &'a mut LookupTable,
|
lookup: &'a mut LookupTable,
|
||||||
goal: &'a Type,
|
|
||||||
) -> impl Iterator<Item = TypeTree> + 'a {
|
) -> impl Iterator<Item = TypeTree> + 'a {
|
||||||
|
let db = ctx.sema.db;
|
||||||
|
let module = ctx.scope.module();
|
||||||
fn variant_helper(
|
fn variant_helper(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
lookup: &mut LookupTable,
|
lookup: &mut LookupTable,
|
||||||
parent_enum: Enum,
|
parent_enum: Enum,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
goal: &Type,
|
goal: &Type,
|
||||||
|
config: &TermSearchConfig,
|
||||||
) -> Vec<(Type, Vec<TypeTree>)> {
|
) -> Vec<(Type, Vec<TypeTree>)> {
|
||||||
let generics = GenericDef::from(variant.parent_enum(db));
|
let generics = GenericDef::from(variant.parent_enum(db));
|
||||||
|
|
||||||
|
@ -151,7 +149,7 @@ pub(super) fn type_constructor<'a>(
|
||||||
.permutations(non_default_type_params_len);
|
.permutations(non_default_type_params_len);
|
||||||
|
|
||||||
generic_params
|
generic_params
|
||||||
.filter_map(|generics| {
|
.filter_map(move |generics| {
|
||||||
// Insert default type params
|
// Insert default type params
|
||||||
let mut g = generics.into_iter();
|
let mut g = generics.into_iter();
|
||||||
let generics: Vec<_> = type_params
|
let generics: Vec<_> = type_params
|
||||||
|
@ -171,7 +169,7 @@ pub(super) fn type_constructor<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore types that have something to do with lifetimes
|
// 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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,9 +209,10 @@ pub(super) fn type_constructor<'a>(
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
defs.iter()
|
defs.iter()
|
||||||
.filter_map(|def| match def {
|
.filter_map(move |def| match def {
|
||||||
ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
|
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() {
|
if variant_trees.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -224,7 +223,9 @@ pub(super) fn type_constructor<'a>(
|
||||||
let trees: Vec<(Type, Vec<TypeTree>)> = enum_
|
let trees: Vec<(Type, Vec<TypeTree>)> = enum_
|
||||||
.variants(db)
|
.variants(db)
|
||||||
.into_iter()
|
.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();
|
.collect();
|
||||||
|
|
||||||
if !trees.is_empty() {
|
if !trees.is_empty() {
|
||||||
|
@ -234,8 +235,8 @@ pub(super) fn type_constructor<'a>(
|
||||||
Some(trees)
|
Some(trees)
|
||||||
}
|
}
|
||||||
ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => {
|
ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => {
|
||||||
// Ignore unstable
|
// Ignore unstable and not visible
|
||||||
if it.is_unstable(db) {
|
if it.is_unstable(db) || !it.is_visible_from(db, module) {
|
||||||
return None;
|
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
|
// Allow types with generics only if they take us straight to goal for
|
||||||
// performance reasons
|
// performance reasons
|
||||||
if non_default_type_params_len != 0
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore types that have something to do with lifetimes
|
// 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;
|
return None;
|
||||||
}
|
}
|
||||||
let fileds = it.fields(db);
|
let fileds = it.fields(db);
|
||||||
// Check if all fields are visible, otherwise we cannot fill them
|
// 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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +336,7 @@ pub(super) fn type_constructor<'a>(
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.flatten()
|
.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()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,20 +349,18 @@ pub(super) fn type_constructor<'a>(
|
||||||
/// elements that unify with `goal`.
|
/// elements that unify with `goal`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `db` - HIR database
|
/// * `ctx` - Context for the term search
|
||||||
/// * `module` - Module where the term search target location
|
|
||||||
/// * `defs` - Set of items in scope at term search target location
|
/// * `defs` - Set of items in scope at term search target location
|
||||||
/// * `lookup` - Lookup table for types
|
/// * `lookup` - Lookup table for types
|
||||||
/// * `goal` - Term search target type
|
pub(super) fn free_function<'a, DB: HirDatabase>(
|
||||||
pub(super) fn free_function<'a>(
|
ctx: &'a TermSearchCtx<'a, DB>,
|
||||||
db: &'a dyn HirDatabase,
|
|
||||||
module: &'a Module,
|
|
||||||
defs: &'a FxHashSet<ScopeDef>,
|
defs: &'a FxHashSet<ScopeDef>,
|
||||||
lookup: &'a mut LookupTable,
|
lookup: &'a mut LookupTable,
|
||||||
goal: &'a Type,
|
|
||||||
) -> impl Iterator<Item = TypeTree> + 'a {
|
) -> impl Iterator<Item = TypeTree> + 'a {
|
||||||
|
let db = ctx.sema.db;
|
||||||
|
let module = ctx.scope.module();
|
||||||
defs.iter()
|
defs.iter()
|
||||||
.filter_map(|def| match def {
|
.filter_map(move |def| match def {
|
||||||
ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
|
ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
|
||||||
let generics = GenericDef::from(*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());
|
let ret_ty = it.ret_type_with_generics(db, generics.iter().cloned());
|
||||||
// Filter out private and unsafe functions
|
// 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_unsafe_to_call(db)
|
||||||
|| it.is_unstable(db)
|
|| it.is_unstable(db)
|
||||||
|| ret_ty.contains_reference(db)
|
|| ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
|
||||||
|| ret_ty.is_raw_ptr()
|
|| ret_ty.is_raw_ptr()
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
|
@ -461,7 +460,7 @@ pub(super) fn free_function<'a>(
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.flatten()
|
.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()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,18 +475,16 @@ pub(super) fn free_function<'a>(
|
||||||
/// elements that unify with `goal`.
|
/// elements that unify with `goal`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `db` - HIR database
|
/// * `ctx` - Context for the term search
|
||||||
/// * `module` - Module where the term search target location
|
|
||||||
/// * `defs` - Set of items in scope at term search target location
|
/// * `defs` - Set of items in scope at term search target location
|
||||||
/// * `lookup` - Lookup table for types
|
/// * `lookup` - Lookup table for types
|
||||||
/// * `goal` - Term search target type
|
pub(super) fn impl_method<'a, DB: HirDatabase>(
|
||||||
pub(super) fn impl_method<'a>(
|
ctx: &'a TermSearchCtx<'a, DB>,
|
||||||
db: &'a dyn HirDatabase,
|
|
||||||
module: &'a Module,
|
|
||||||
_defs: &'a FxHashSet<ScopeDef>,
|
_defs: &'a FxHashSet<ScopeDef>,
|
||||||
lookup: &'a mut LookupTable,
|
lookup: &'a mut LookupTable,
|
||||||
goal: &'a Type,
|
|
||||||
) -> impl Iterator<Item = TypeTree> + 'a {
|
) -> impl Iterator<Item = TypeTree> + 'a {
|
||||||
|
let db = ctx.sema.db;
|
||||||
|
let module = ctx.scope.module();
|
||||||
lookup
|
lookup
|
||||||
.new_types(NewTypesKey::ImplMethod)
|
.new_types(NewTypesKey::ImplMethod)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -499,7 +496,7 @@ pub(super) fn impl_method<'a>(
|
||||||
AssocItem::Function(f) => Some((imp, ty, f)),
|
AssocItem::Function(f) => Some((imp, ty, f)),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.filter_map(|(imp, ty, it)| {
|
.filter_map(move |(imp, ty, it)| {
|
||||||
let fn_generics = GenericDef::from(it);
|
let fn_generics = GenericDef::from(it);
|
||||||
let imp_generics = GenericDef::from(imp);
|
let imp_generics = GenericDef::from(imp);
|
||||||
|
|
||||||
|
@ -520,7 +517,7 @@ pub(super) fn impl_method<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out private and unsafe functions
|
// 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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +567,9 @@ pub(super) fn impl_method<'a>(
|
||||||
ty.type_arguments().chain(generics.iter().cloned()),
|
ty.type_arguments().chain(generics.iter().cloned()),
|
||||||
);
|
);
|
||||||
// Filter out functions that return references
|
// 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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,7 +614,7 @@ pub(super) fn impl_method<'a>(
|
||||||
Some(trees)
|
Some(trees)
|
||||||
})
|
})
|
||||||
.flatten()
|
.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()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,24 +626,21 @@ pub(super) fn impl_method<'a>(
|
||||||
/// elements that unify with `goal`.
|
/// elements that unify with `goal`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `db` - HIR database
|
/// * `ctx` - Context for the term search
|
||||||
/// * `module` - Module where the term search target location
|
|
||||||
/// * `defs` - Set of items in scope at term search target location
|
/// * `defs` - Set of items in scope at term search target location
|
||||||
/// * `lookup` - Lookup table for types
|
/// * `lookup` - Lookup table for types
|
||||||
/// * `goal` - Term search target type
|
pub(super) fn struct_projection<'a, DB: HirDatabase>(
|
||||||
pub(super) fn struct_projection<'a>(
|
ctx: &'a TermSearchCtx<'a, DB>,
|
||||||
db: &'a dyn HirDatabase,
|
|
||||||
module: &'a Module,
|
|
||||||
_defs: &'a FxHashSet<ScopeDef>,
|
_defs: &'a FxHashSet<ScopeDef>,
|
||||||
lookup: &'a mut LookupTable,
|
lookup: &'a mut LookupTable,
|
||||||
goal: &'a Type,
|
|
||||||
) -> impl Iterator<Item = TypeTree> + 'a {
|
) -> impl Iterator<Item = TypeTree> + 'a {
|
||||||
|
let db = ctx.sema.db;
|
||||||
|
let module = ctx.scope.module();
|
||||||
lookup
|
lookup
|
||||||
.new_types(NewTypesKey::StructProjection)
|
.new_types(NewTypesKey::StructProjection)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ty| (ty.clone(), lookup.find(db, &ty).expect("TypeTree not in lookup")))
|
.map(|ty| (ty.clone(), lookup.find(db, &ty).expect("TypeTree not in lookup")))
|
||||||
.flat_map(move |(ty, targets)| {
|
.flat_map(move |(ty, targets)| {
|
||||||
let module = module.clone();
|
|
||||||
ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| {
|
ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| {
|
||||||
if !field.is_visible_from(db, module) {
|
if !field.is_visible_from(db, module) {
|
||||||
return None;
|
return None;
|
||||||
|
@ -656,7 +652,7 @@ pub(super) fn struct_projection<'a>(
|
||||||
Some((filed_ty, trees))
|
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()
|
.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_
|
/// _Note that there is no point of calling it iteratively as the output is always the same_
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `db` - HIR database
|
/// * `ctx` - Context for the term search
|
||||||
/// * `module` - Module where the term search target location
|
|
||||||
/// * `defs` - Set of items in scope at term search target location
|
/// * `defs` - Set of items in scope at term search target location
|
||||||
/// * `lookup` - Lookup table for types
|
/// * `lookup` - Lookup table for types
|
||||||
/// * `goal` - Term search target type
|
pub(super) fn famous_types<'a, DB: HirDatabase>(
|
||||||
pub(super) fn famous_types<'a>(
|
ctx: &'a TermSearchCtx<'a, DB>,
|
||||||
db: &'a dyn HirDatabase,
|
|
||||||
module: &'a Module,
|
|
||||||
_defs: &'a FxHashSet<ScopeDef>,
|
_defs: &'a FxHashSet<ScopeDef>,
|
||||||
lookup: &'a mut LookupTable,
|
lookup: &'a mut LookupTable,
|
||||||
goal: &'a Type,
|
|
||||||
) -> impl Iterator<Item = TypeTree> + 'a {
|
) -> impl Iterator<Item = TypeTree> + '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: "true" },
|
||||||
TypeTree::FamousType { ty: Type::new(db, module.id, TyBuilder::bool()), value: "false" },
|
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()));
|
lookup.insert(tt.ty(db), std::iter::once(tt.clone()));
|
||||||
tt
|
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
|
/// # Impl static method (without self type) tactic
|
||||||
|
@ -703,22 +697,20 @@ pub(super) fn famous_types<'a>(
|
||||||
/// elements that unify with `goal`.
|
/// elements that unify with `goal`.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `db` - HIR database
|
/// * `ctx` - Context for the term search
|
||||||
/// * `module` - Module where the term search target location
|
|
||||||
/// * `defs` - Set of items in scope at term search target location
|
/// * `defs` - Set of items in scope at term search target location
|
||||||
/// * `lookup` - Lookup table for types
|
/// * `lookup` - Lookup table for types
|
||||||
/// * `goal` - Term search target type
|
pub(super) fn impl_static_method<'a, DB: HirDatabase>(
|
||||||
pub(super) fn impl_static_method<'a>(
|
ctx: &'a TermSearchCtx<'a, DB>,
|
||||||
db: &'a dyn HirDatabase,
|
|
||||||
module: &'a Module,
|
|
||||||
_defs: &'a FxHashSet<ScopeDef>,
|
_defs: &'a FxHashSet<ScopeDef>,
|
||||||
lookup: &'a mut LookupTable,
|
lookup: &'a mut LookupTable,
|
||||||
goal: &'a Type,
|
|
||||||
) -> impl Iterator<Item = TypeTree> + 'a {
|
) -> impl Iterator<Item = TypeTree> + 'a {
|
||||||
|
let db = ctx.sema.db;
|
||||||
|
let module = ctx.scope.module();
|
||||||
lookup
|
lookup
|
||||||
.take_types_wishlist()
|
.take_types_wishlist()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(iter::once(goal.clone()))
|
.chain(iter::once(ctx.goal.clone()))
|
||||||
.flat_map(|ty| {
|
.flat_map(|ty| {
|
||||||
Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
|
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)),
|
AssocItem::Function(f) => Some((imp, ty, f)),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.filter_map(|(imp, ty, it)| {
|
.filter_map(move |(imp, ty, it)| {
|
||||||
let fn_generics = GenericDef::from(it);
|
let fn_generics = GenericDef::from(it);
|
||||||
let imp_generics = GenericDef::from(imp);
|
let imp_generics = GenericDef::from(imp);
|
||||||
|
|
||||||
|
@ -751,7 +743,7 @@ pub(super) fn impl_static_method<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out private and unsafe functions
|
// 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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,7 +793,9 @@ pub(super) fn impl_static_method<'a>(
|
||||||
ty.type_arguments().chain(generics.iter().cloned()),
|
ty.type_arguments().chain(generics.iter().cloned()),
|
||||||
);
|
);
|
||||||
// Filter out functions that return references
|
// 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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -845,6 +839,6 @@ pub(super) fn impl_static_method<'a>(
|
||||||
Some(trees)
|
Some(trees)
|
||||||
})
|
})
|
||||||
.flatten()
|
.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()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! Term search assist
|
//! Term search assist
|
||||||
|
use hir::term_search::TermSearchCtx;
|
||||||
use ide_db::assists::{AssistId, AssistKind, GroupLabel};
|
use ide_db::assists::{AssistId, AssistKind, GroupLabel};
|
||||||
|
|
||||||
use itertools::Itertools;
|
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 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() {
|
if paths.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -41,6 +41,7 @@ use crate::{
|
||||||
macro_::render_macro,
|
macro_::render_macro,
|
||||||
pattern::{render_struct_pat, render_variant_pat},
|
pattern::{render_struct_pat, render_variant_pat},
|
||||||
render_field, render_path_resolution, render_pattern_resolution, render_tuple_field,
|
render_field, render_path_resolution, render_pattern_resolution, render_tuple_field,
|
||||||
|
render_type_tree,
|
||||||
type_alias::{render_type_alias, render_type_alias_with_eq},
|
type_alias::{render_type_alias, render_type_alias_with_eq},
|
||||||
union_literal::render_union_literal,
|
union_literal::render_union_literal,
|
||||||
RenderContext,
|
RenderContext,
|
||||||
|
@ -157,6 +158,16 @@ impl Completions {
|
||||||
item.add_to(self, ctx.db);
|
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(
|
pub(crate) fn add_crate_roots(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &CompletionContext<'_>,
|
ctx: &CompletionContext<'_>,
|
||||||
|
@ -690,6 +701,7 @@ pub(super) fn complete_name_ref(
|
||||||
match kind {
|
match kind {
|
||||||
NameRefKind::Path(path_ctx) => {
|
NameRefKind::Path(path_ctx) => {
|
||||||
flyimport::import_on_the_fly_path(acc, ctx, path_ctx);
|
flyimport::import_on_the_fly_path(acc, ctx, path_ctx);
|
||||||
|
expr::complete_expr(acc, ctx, path_ctx);
|
||||||
|
|
||||||
match &path_ctx.kind {
|
match &path_ctx.kind {
|
||||||
PathKind::Expr { expr_ctx } => {
|
PathKind::Expr { expr_ctx } => {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
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(
|
fn scope_def_to_name(
|
||||||
resolution: ScopeDef,
|
resolution: ScopeDef,
|
||||||
ctx: &RenderContext<'_>,
|
ctx: &RenderContext<'_>,
|
||||||
|
|
|
@ -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::{
|
use ide_db::{
|
||||||
assists::{Assist, AssistId, AssistKind, GroupLabel},
|
assists::{Assist, AssistId, AssistKind, GroupLabel},
|
||||||
label::Label,
|
label::Label,
|
||||||
|
@ -40,7 +44,8 @@ fn fixes(sema: &Semantics<'_, RootDatabase>, d: &hir::TypedHole) -> Option<Vec<A
|
||||||
d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?;
|
d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?;
|
||||||
let scope = sema.scope(d.expr.value.to_node(&root).syntax())?;
|
let scope = sema.scope(d.expr.value.to_node(&root).syntax())?;
|
||||||
|
|
||||||
let paths = term_search(sema, &scope, &d.expected);
|
let ctx = TermSearchCtx { sema, scope: &scope, goal: d.expected.clone(), config: Default::default() };
|
||||||
|
let paths = term_search(ctx);
|
||||||
|
|
||||||
let mut assists = vec![];
|
let mut assists = vec![];
|
||||||
for path in paths.into_iter().unique() {
|
for path in paths.into_iter().unique() {
|
||||||
|
|
|
@ -406,7 +406,15 @@ impl flags::AnalysisStats {
|
||||||
None => continue,
|
None => 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() {
|
if found_terms.is_empty() {
|
||||||
acc.tail_expr_no_term += 1;
|
acc.tail_expr_no_term += 1;
|
||||||
|
|
Loading…
Reference in a new issue