mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
Add tactic for associated item constants
This commit is contained in:
parent
021ae0101c
commit
c87609fef1
5 changed files with 118 additions and 20 deletions
|
@ -325,6 +325,7 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
|
|||
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));
|
||||
solutions.extend(tactics::assoc_const(ctx, &defs, &mut lookup));
|
||||
|
||||
while should_continue() {
|
||||
lookup.new_round();
|
||||
|
|
|
@ -9,8 +9,8 @@ use hir_ty::{
|
|||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
Adt, AsAssocItem, Const, ConstParam, Field, Function, GenericDef, Local, ModuleDef,
|
||||
SemanticsScope, Static, Struct, StructKind, Trait, Type, Variant,
|
||||
Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Field, Function, GenericDef, Local,
|
||||
ModuleDef, SemanticsScope, Static, Struct, StructKind, Trait, Type, Variant,
|
||||
};
|
||||
|
||||
/// Helper function to get path to `ModuleDef`
|
||||
|
@ -138,7 +138,17 @@ impl Expr {
|
|||
let db = sema_scope.db;
|
||||
let mod_item_path_str = |s, def| mod_item_path_str(s, def, cfg);
|
||||
match self {
|
||||
Expr::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
|
||||
Expr::Const(it) => match it.as_assoc_item(db).map(|it| it.container(db)) {
|
||||
Some(container) => {
|
||||
let container_name = container_name(container, sema_scope, cfg)?;
|
||||
let const_name = it
|
||||
.name(db)
|
||||
.map(|c| c.display(db.upcast()).to_string())
|
||||
.unwrap_or(String::new());
|
||||
Ok(format!("{container_name}::{const_name}"))
|
||||
}
|
||||
None => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
|
||||
},
|
||||
Expr::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)),
|
||||
Expr::Local(it) => Ok(it.name(db).display(db.upcast()).to_string()),
|
||||
Expr::ConstParam(it) => Ok(it.name(db).display(db.upcast()).to_string()),
|
||||
|
@ -153,22 +163,7 @@ impl Expr {
|
|||
|
||||
match func.as_assoc_item(db).map(|it| it.container(db)) {
|
||||
Some(container) => {
|
||||
let container_name = match container {
|
||||
crate::AssocItemContainer::Trait(trait_) => {
|
||||
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))?
|
||||
}
|
||||
crate::AssocItemContainer::Impl(imp) => {
|
||||
let self_ty = imp.self_ty(db);
|
||||
// Should it be guaranteed that `mod_item_path` always exists?
|
||||
match self_ty
|
||||
.as_adt()
|
||||
.and_then(|adt| mod_item_path(sema_scope, &adt.into(), cfg))
|
||||
{
|
||||
Some(path) => path.display(sema_scope.db.upcast()).to_string(),
|
||||
None => self_ty.display(db).to_string(),
|
||||
}
|
||||
}
|
||||
};
|
||||
let container_name = container_name(container, sema_scope, cfg)?;
|
||||
let fn_name = func.name(db).display(db.upcast()).to_string();
|
||||
Ok(format!("{container_name}::{fn_name}({args})"))
|
||||
}
|
||||
|
@ -414,3 +409,25 @@ impl Expr {
|
|||
matches!(self, Expr::Many(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to find name of container
|
||||
fn container_name(
|
||||
container: AssocItemContainer,
|
||||
sema_scope: &SemanticsScope<'_>,
|
||||
cfg: ImportPathConfig,
|
||||
) -> Result<String, DisplaySourceCodeError> {
|
||||
let container_name = match container {
|
||||
crate::AssocItemContainer::Trait(trait_) => {
|
||||
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_), cfg)?
|
||||
}
|
||||
crate::AssocItemContainer::Impl(imp) => {
|
||||
let self_ty = imp.self_ty(sema_scope.db);
|
||||
// Should it be guaranteed that `mod_item_path` always exists?
|
||||
match self_ty.as_adt().and_then(|adt| mod_item_path(sema_scope, &adt.into(), cfg)) {
|
||||
Some(path) => path.display(sema_scope.db.upcast()).to_string(),
|
||||
None => self_ty.display(sema_scope.db).to_string(),
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(container_name)
|
||||
}
|
||||
|
|
|
@ -80,7 +80,10 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
|
|||
lookup.insert(ty.clone(), std::iter::once(expr.clone()));
|
||||
|
||||
// Don't suggest local references as they are not valid for return
|
||||
if matches!(expr, Expr::Local(_)) && ty.contains_reference(db) {
|
||||
if matches!(expr, Expr::Local(_))
|
||||
&& ty.contains_reference(db)
|
||||
&& ctx.config.enable_borrowcheck
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -88,6 +91,52 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
|
|||
})
|
||||
}
|
||||
|
||||
/// # Associated constant tactic
|
||||
///
|
||||
/// Attempts to fulfill the goal by trying constants defined as associated items.
|
||||
/// Only considers them on types that are in scope.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ctx` - Context for the term search
|
||||
/// * `defs` - Set of items in scope at term search target location
|
||||
/// * `lookup` - Lookup table for types
|
||||
///
|
||||
/// 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 assoc_const<'a, DB: HirDatabase>(
|
||||
ctx: &'a TermSearchCtx<'a, DB>,
|
||||
defs: &'a FxHashSet<ScopeDef>,
|
||||
lookup: &'a mut LookupTable,
|
||||
) -> impl Iterator<Item = Expr> + 'a {
|
||||
let db = ctx.sema.db;
|
||||
let module = ctx.scope.module();
|
||||
|
||||
defs.iter()
|
||||
.filter_map(|def| match def {
|
||||
ScopeDef::ModuleDef(ModuleDef::Adt(it)) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.flat_map(|it| Impl::all_for_type(db, it.ty(db)))
|
||||
.filter(|it| !it.is_unsafe(db))
|
||||
.flat_map(|it| it.items(db))
|
||||
.filter(move |it| it.is_visible_from(db, module))
|
||||
.filter_map(AssocItem::as_const)
|
||||
.filter_map(|it| {
|
||||
let expr = Expr::Const(it);
|
||||
let ty = it.ty(db);
|
||||
|
||||
if ty.contains_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
lookup.insert(ty.clone(), std::iter::once(expr.clone()));
|
||||
|
||||
ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr)
|
||||
})
|
||||
}
|
||||
|
||||
/// # Data constructor tactic
|
||||
///
|
||||
/// Attempts different data constructors for enums and structs in scope
|
||||
|
|
|
@ -290,4 +290,34 @@ fn f() { let a = 1; let b: Foo<i32> = todo$0!(); }"#,
|
|||
fn f() { let a = 1; let b: Foo<i32> = Foo(a); }"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_assoc_item() {
|
||||
check_assist(
|
||||
term_search,
|
||||
r#"//- minicore: todo, unimplemented
|
||||
struct Foo;
|
||||
impl Foo { const FOO: i32 = 0; }
|
||||
fn f() { let a: i32 = todo$0!(); }"#,
|
||||
r#"struct Foo;
|
||||
impl Foo { const FOO: i32 = 0; }
|
||||
fn f() { let a: i32 = Foo::FOO; }"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trait_assoc_item() {
|
||||
check_assist(
|
||||
term_search,
|
||||
r#"//- minicore: todo, unimplemented
|
||||
struct Foo;
|
||||
trait Bar { const BAR: i32; }
|
||||
impl Bar for Foo { const BAR: i32 = 0; }
|
||||
fn f() { let a: i32 = todo$0!(); }"#,
|
||||
r#"struct Foo;
|
||||
trait Bar { const BAR: i32; }
|
||||
impl Bar for Foo { const BAR: i32 = 0; }
|
||||
fn f() { let a: i32 = Foo::BAR; }"#,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1799,6 +1799,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
|
|||
"#,
|
||||
expect![[r#"
|
||||
lc world [type+name+local]
|
||||
ex world [type]
|
||||
st WorldSnapshot {…} []
|
||||
st &WorldSnapshot {…} [type]
|
||||
st WorldSnapshot []
|
||||
|
|
Loading…
Reference in a new issue