mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Refactor to unify with method resolution
This commit is contained in:
parent
c7cedea270
commit
1173c3dab5
7 changed files with 117 additions and 141 deletions
|
@ -1053,4 +1053,13 @@ impl AssocItem {
|
|||
AssocItem::TypeAlias(t) => t.module(db),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn container(self, db: &impl DefDatabase) -> Container {
|
||||
match self {
|
||||
AssocItem::Function(f) => f.container(db),
|
||||
AssocItem::Const(c) => c.container(db),
|
||||
AssocItem::TypeAlias(t) => t.container(db),
|
||||
}
|
||||
.expect("AssocItem without container")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,9 +77,10 @@ impl GenericParams {
|
|||
let parent = match def {
|
||||
GenericDef::Function(it) => it.container(db).map(GenericDef::from),
|
||||
GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from),
|
||||
GenericDef::Const(it) => it.container(db).map(GenericDef::from),
|
||||
GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()),
|
||||
GenericDef::Adt(_) | GenericDef::Trait(_) => None,
|
||||
GenericDef::ImplBlock(_) | GenericDef::Const(_) => None,
|
||||
GenericDef::ImplBlock(_) => None,
|
||||
};
|
||||
let mut generics = GenericParams {
|
||||
def,
|
||||
|
|
|
@ -28,8 +28,8 @@ use crate::{
|
|||
ids::LocationCtx,
|
||||
resolve::{ScopeDef, TypeNs, ValueNs},
|
||||
ty::method_resolution::implements_trait,
|
||||
Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module,
|
||||
Name, Path, Resolver, Static, Struct, Ty,
|
||||
AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId,
|
||||
MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty,
|
||||
};
|
||||
|
||||
fn try_get_resolver_for_node(
|
||||
|
@ -327,7 +327,7 @@ impl SourceAnalyzer {
|
|||
db: &impl HirDatabase,
|
||||
ty: Ty,
|
||||
name: Option<&Name>,
|
||||
callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
// There should be no inference vars in types passed here
|
||||
// FIXME check that?
|
||||
|
@ -337,6 +337,7 @@ impl SourceAnalyzer {
|
|||
db,
|
||||
&self.resolver,
|
||||
name,
|
||||
crate::ty::method_resolution::LookupMode::MethodCall,
|
||||
callback,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,11 +6,9 @@ use super::{ExprOrPatId, InferenceContext, TraitRef};
|
|||
use crate::{
|
||||
db::HirDatabase,
|
||||
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||
ty::{lower, traits::TraitEnvironment, Canonical},
|
||||
ty::{Substs, Ty, TypableDef, TypeWalk},
|
||||
AssocItem, HasGenericParams, Name, Namespace, Path, Trait,
|
||||
ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk},
|
||||
AssocItem, Container, HasGenericParams, Name, Namespace, Path,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
pub(super) fn infer_path(
|
||||
|
@ -184,91 +182,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
return None;
|
||||
}
|
||||
|
||||
self.find_inherent_assoc_candidate(ty.clone(), name, id)
|
||||
.or_else(|| self.find_trait_assoc_candidate(ty.clone(), name, id))
|
||||
}
|
||||
|
||||
fn find_inherent_assoc_candidate(
|
||||
&mut self,
|
||||
ty: Ty,
|
||||
name: &Name,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<Substs>)> {
|
||||
let krate = self.resolver.krate()?;
|
||||
|
||||
// Find impl
|
||||
let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item {
|
||||
AssocItem::Function(func) => {
|
||||
if *name == func.name(self.db) {
|
||||
Some(AssocItem::Function(func))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
AssocItem::Const(konst) => {
|
||||
if konst.name(self.db).map_or(false, |n| n == *name) {
|
||||
Some(AssocItem::Const(konst))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
AssocItem::TypeAlias(_) => None,
|
||||
})?;
|
||||
let def = match item {
|
||||
AssocItem::Function(f) => ValueNs::Function(f),
|
||||
AssocItem::Const(c) => ValueNs::Const(c),
|
||||
AssocItem::TypeAlias(_) => unreachable!(),
|
||||
};
|
||||
let substs = self.find_self_types(&def, ty);
|
||||
|
||||
self.write_assoc_resolution(id, item);
|
||||
Some((def, substs))
|
||||
}
|
||||
|
||||
fn find_trait_assoc_candidate(
|
||||
&mut self,
|
||||
ty: Ty,
|
||||
name: &Name,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<Substs>)> {
|
||||
let krate = self.resolver.krate()?;
|
||||
|
||||
let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
|
||||
|
||||
let env = lower::trait_env(self.db, &self.resolver);
|
||||
// if we have `T: Trait` in the param env, the trait doesn't need to be in scope
|
||||
let traits_from_env = env
|
||||
.trait_predicates_for_self_ty(&ty)
|
||||
.map(|tr| tr.trait_)
|
||||
.flat_map(|t| t.all_super_traits(self.db));
|
||||
let traits = traits_from_env.chain(self.resolver.traits_in_scope(self.db));
|
||||
|
||||
'traits: for t in traits {
|
||||
let data = t.trait_data(self.db);
|
||||
let mut known_implemented = false;
|
||||
for item in data.items() {
|
||||
if let AssocItem::Function(f) = *item {
|
||||
if f.name(self.db) == *name {
|
||||
if !known_implemented {
|
||||
let goal = generic_implements_goal(
|
||||
self.db,
|
||||
env.clone(),
|
||||
t,
|
||||
canonical_ty.value.clone(),
|
||||
);
|
||||
if self.db.trait_solve(krate, goal).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
known_implemented = true;
|
||||
method_resolution::iterate_method_candidates(
|
||||
&canonical_ty.value,
|
||||
self.db,
|
||||
&self.resolver.clone(),
|
||||
Some(name),
|
||||
method_resolution::LookupMode::Path,
|
||||
move |_ty, item| {
|
||||
let def = match item {
|
||||
AssocItem::Function(f) => ValueNs::Function(f),
|
||||
AssocItem::Const(c) => ValueNs::Const(c),
|
||||
AssocItem::TypeAlias(_) => unreachable!(),
|
||||
};
|
||||
match item.container(self.db) {
|
||||
Container::ImplBlock(_) => {
|
||||
let substs = self.find_self_types(&def, ty.clone());
|
||||
|
||||
self.write_assoc_resolution(id, item);
|
||||
Some((def, substs))
|
||||
}
|
||||
Container::Trait(t) => {
|
||||
// we're picking this method
|
||||
let trait_substs = Substs::build_for_def(self.db, t)
|
||||
.push(ty.clone())
|
||||
.fill(std::iter::repeat_with(|| self.new_type_var()))
|
||||
.build();
|
||||
let substs = Substs::build_for_def(self.db, f)
|
||||
let substs = Substs::build_for_def(self.db, item)
|
||||
.use_parent_substs(&trait_substs)
|
||||
.fill_with_params()
|
||||
.build();
|
||||
|
@ -277,14 +218,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
substs: trait_substs,
|
||||
}));
|
||||
|
||||
self.write_assoc_resolution(id, *item);
|
||||
return Some((ValueNs::Function(f), Some(substs)));
|
||||
self.write_assoc_resolution(id, item);
|
||||
Some((def, Some(substs)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {
|
||||
|
@ -317,23 +256,3 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove duplication
|
||||
/// This creates Substs for a trait with the given Self type and type variables
|
||||
/// for all other parameters, to query Chalk with it.
|
||||
fn generic_implements_goal(
|
||||
db: &impl HirDatabase,
|
||||
env: Arc<TraitEnvironment>,
|
||||
trait_: Trait,
|
||||
self_ty: Canonical<Ty>,
|
||||
) -> Canonical<super::InEnvironment<super::Obligation>> {
|
||||
let num_vars = self_ty.num_vars;
|
||||
let substs = super::Substs::build_for_def(db, trait_)
|
||||
.push(self_ty.value)
|
||||
.fill_with_bound_vars(num_vars as u32)
|
||||
.build();
|
||||
let num_vars = substs.len() - 1 + self_ty.num_vars;
|
||||
let trait_ref = TraitRef { trait_, substs };
|
||||
let obligation = super::Obligation::Trait(trait_ref);
|
||||
Canonical { num_vars, value: super::InEnvironment::new(env, obligation) }
|
||||
}
|
||||
|
|
|
@ -166,7 +166,19 @@ pub(crate) fn lookup_method(
|
|||
name: &Name,
|
||||
resolver: &Resolver,
|
||||
) -> Option<(Ty, Function)> {
|
||||
iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f)))
|
||||
iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| {
|
||||
if let AssocItem::Function(f) = f {
|
||||
Some((ty.clone(), f))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum LookupMode {
|
||||
MethodCall,
|
||||
Path,
|
||||
}
|
||||
|
||||
// This would be nicer if it just returned an iterator, but that runs into
|
||||
|
@ -176,7 +188,8 @@ pub(crate) fn iterate_method_candidates<T>(
|
|||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
mode: LookupMode,
|
||||
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
// For method calls, rust first does any number of autoderef, and then one
|
||||
// autoref (i.e. when the method takes &self or &mut self). We just ignore
|
||||
|
@ -188,13 +201,15 @@ pub(crate) fn iterate_method_candidates<T>(
|
|||
// rustc does an autoderef and then autoref again).
|
||||
|
||||
let krate = resolver.krate()?;
|
||||
// TODO no autoderef in LookupMode::Path
|
||||
for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
|
||||
if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback)
|
||||
if let Some(result) =
|
||||
iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
if let Some(result) =
|
||||
iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback)
|
||||
iterate_trait_method_candidates(&derefed_ty, db, resolver, name, mode, &mut callback)
|
||||
{
|
||||
return Some(result);
|
||||
}
|
||||
|
@ -207,7 +222,8 @@ fn iterate_trait_method_candidates<T>(
|
|||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
name: Option<&Name>,
|
||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
mode: LookupMode,
|
||||
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let krate = resolver.krate()?;
|
||||
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
|
||||
|
@ -231,21 +247,35 @@ fn iterate_trait_method_candidates<T>(
|
|||
// trait, but if we find out it doesn't, we'll skip the rest of the
|
||||
// iteration
|
||||
let mut known_implemented = inherently_implemented;
|
||||
for item in data.items() {
|
||||
if let AssocItem::Function(m) = *item {
|
||||
let data = m.data(db);
|
||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
||||
if !known_implemented {
|
||||
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
||||
if db.trait_solve(krate, goal).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
known_implemented = true;
|
||||
if let Some(result) = callback(&ty.value, m) {
|
||||
return Some(result);
|
||||
for &item in data.items() {
|
||||
// TODO unify with the impl case
|
||||
match item {
|
||||
AssocItem::Function(m) => {
|
||||
let data = m.data(db);
|
||||
if !name.map_or(true, |name| data.name() == name)
|
||||
|| (!data.has_self_param() && mode != LookupMode::Path)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
AssocItem::Const(c) => {
|
||||
if !name.map_or(true, |name| Some(name) == c.name(db).as_ref())
|
||||
|| (mode != LookupMode::Path)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
if !known_implemented {
|
||||
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
||||
if db.trait_solve(krate, goal).is_none() {
|
||||
continue 'traits;
|
||||
}
|
||||
}
|
||||
known_implemented = true;
|
||||
if let Some(result) = callback(&ty.value, item) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,21 +286,35 @@ fn iterate_inherent_methods<T>(
|
|||
ty: &Canonical<Ty>,
|
||||
db: &impl HirDatabase,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
krate: Crate,
|
||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
for krate in def_crates(db, krate, &ty.value)? {
|
||||
let impls = db.impls_in_crate(krate);
|
||||
|
||||
for impl_block in impls.lookup_impl_blocks(&ty.value) {
|
||||
for item in impl_block.items(db) {
|
||||
if let AssocItem::Function(f) = item {
|
||||
let data = f.data(db);
|
||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
||||
if let Some(result) = callback(&ty.value, f) {
|
||||
return Some(result);
|
||||
match item {
|
||||
AssocItem::Function(f) => {
|
||||
let data = f.data(db);
|
||||
if !name.map_or(true, |name| data.name() == name)
|
||||
|| (!data.has_self_param() && mode != LookupMode::Path)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
AssocItem::Const(c) => {
|
||||
if !name.map_or(true, |name| Some(name) == c.name(db).as_ref())
|
||||
|| (mode != LookupMode::Path)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some(result) = callback(&ty.value, item) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1841,8 +1841,8 @@ fn test() {
|
|||
[243; 254) 'Struct::FOO': u32
|
||||
[264; 265) 'y': u32
|
||||
[268; 277) 'Enum::BAR': u32
|
||||
[287; 288) 'z': {unknown}
|
||||
[291; 304) 'TraitTest::ID': {unknown}
|
||||
[287; 288) 'z': u32
|
||||
[291; 304) 'TraitTest::ID': u32
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
|
@ -58,10 +58,12 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
|
|||
|
||||
fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
|
||||
let mut seen_methods = FxHashSet::default();
|
||||
ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| {
|
||||
let data = func.data(ctx.db);
|
||||
if data.has_self_param() && seen_methods.insert(data.name().clone()) {
|
||||
acc.add_function(ctx, func);
|
||||
ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, item| {
|
||||
if let hir::AssocItem::Function(func) = item {
|
||||
let data = func.data(ctx.db);
|
||||
if data.has_self_param() && seen_methods.insert(data.name().clone()) {
|
||||
acc.add_function(ctx, func);
|
||||
}
|
||||
}
|
||||
None::<()>
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue