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),
|
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 {
|
let parent = match def {
|
||||||
GenericDef::Function(it) => it.container(db).map(GenericDef::from),
|
GenericDef::Function(it) => it.container(db).map(GenericDef::from),
|
||||||
GenericDef::TypeAlias(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::EnumVariant(it) => Some(it.parent_enum(db).into()),
|
||||||
GenericDef::Adt(_) | GenericDef::Trait(_) => None,
|
GenericDef::Adt(_) | GenericDef::Trait(_) => None,
|
||||||
GenericDef::ImplBlock(_) | GenericDef::Const(_) => None,
|
GenericDef::ImplBlock(_) => None,
|
||||||
};
|
};
|
||||||
let mut generics = GenericParams {
|
let mut generics = GenericParams {
|
||||||
def,
|
def,
|
||||||
|
|
|
@ -28,8 +28,8 @@ use crate::{
|
||||||
ids::LocationCtx,
|
ids::LocationCtx,
|
||||||
resolve::{ScopeDef, TypeNs, ValueNs},
|
resolve::{ScopeDef, TypeNs, ValueNs},
|
||||||
ty::method_resolution::implements_trait,
|
ty::method_resolution::implements_trait,
|
||||||
Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module,
|
AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId,
|
||||||
Name, Path, Resolver, Static, Struct, Ty,
|
MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn try_get_resolver_for_node(
|
fn try_get_resolver_for_node(
|
||||||
|
@ -327,7 +327,7 @@ impl SourceAnalyzer {
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
ty: Ty,
|
ty: Ty,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
callback: impl FnMut(&Ty, Function) -> Option<T>,
|
callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
// There should be no inference vars in types passed here
|
// There should be no inference vars in types passed here
|
||||||
// FIXME check that?
|
// FIXME check that?
|
||||||
|
@ -337,6 +337,7 @@ impl SourceAnalyzer {
|
||||||
db,
|
db,
|
||||||
&self.resolver,
|
&self.resolver,
|
||||||
name,
|
name,
|
||||||
|
crate::ty::method_resolution::LookupMode::MethodCall,
|
||||||
callback,
|
callback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,9 @@ use super::{ExprOrPatId, InferenceContext, TraitRef};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
|
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||||
ty::{lower, traits::TraitEnvironment, Canonical},
|
ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk},
|
||||||
ty::{Substs, Ty, TypableDef, TypeWalk},
|
AssocItem, Container, HasGenericParams, Name, Namespace, Path,
|
||||||
AssocItem, HasGenericParams, Name, Namespace, Path, Trait,
|
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
pub(super) fn infer_path(
|
pub(super) fn infer_path(
|
||||||
|
@ -184,91 +182,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
return None;
|
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 canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
|
||||||
|
|
||||||
let env = lower::trait_env(self.db, &self.resolver);
|
method_resolution::iterate_method_candidates(
|
||||||
// if we have `T: Trait` in the param env, the trait doesn't need to be in scope
|
&canonical_ty.value,
|
||||||
let traits_from_env = env
|
self.db,
|
||||||
.trait_predicates_for_self_ty(&ty)
|
&self.resolver.clone(),
|
||||||
.map(|tr| tr.trait_)
|
Some(name),
|
||||||
.flat_map(|t| t.all_super_traits(self.db));
|
method_resolution::LookupMode::Path,
|
||||||
let traits = traits_from_env.chain(self.resolver.traits_in_scope(self.db));
|
move |_ty, item| {
|
||||||
|
let def = match item {
|
||||||
'traits: for t in traits {
|
AssocItem::Function(f) => ValueNs::Function(f),
|
||||||
let data = t.trait_data(self.db);
|
AssocItem::Const(c) => ValueNs::Const(c),
|
||||||
let mut known_implemented = false;
|
AssocItem::TypeAlias(_) => unreachable!(),
|
||||||
for item in data.items() {
|
};
|
||||||
if let AssocItem::Function(f) = *item {
|
match item.container(self.db) {
|
||||||
if f.name(self.db) == *name {
|
Container::ImplBlock(_) => {
|
||||||
if !known_implemented {
|
let substs = self.find_self_types(&def, ty.clone());
|
||||||
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;
|
|
||||||
|
|
||||||
|
self.write_assoc_resolution(id, item);
|
||||||
|
Some((def, substs))
|
||||||
|
}
|
||||||
|
Container::Trait(t) => {
|
||||||
// we're picking this method
|
// we're picking this method
|
||||||
let trait_substs = Substs::build_for_def(self.db, t)
|
let trait_substs = Substs::build_for_def(self.db, t)
|
||||||
.push(ty.clone())
|
.push(ty.clone())
|
||||||
.fill(std::iter::repeat_with(|| self.new_type_var()))
|
.fill(std::iter::repeat_with(|| self.new_type_var()))
|
||||||
.build();
|
.build();
|
||||||
let substs = Substs::build_for_def(self.db, f)
|
let substs = Substs::build_for_def(self.db, item)
|
||||||
.use_parent_substs(&trait_substs)
|
.use_parent_substs(&trait_substs)
|
||||||
.fill_with_params()
|
.fill_with_params()
|
||||||
.build();
|
.build();
|
||||||
|
@ -277,14 +218,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
substs: trait_substs,
|
substs: trait_substs,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
self.write_assoc_resolution(id, *item);
|
self.write_assoc_resolution(id, item);
|
||||||
return Some((ValueNs::Function(f), Some(substs)));
|
Some((def, Some(substs)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
)
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {
|
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,
|
name: &Name,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
) -> Option<(Ty, Function)> {
|
) -> 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
|
// 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,
|
db: &impl HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mode: LookupMode,
|
||||||
|
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
// For method calls, rust first does any number of autoderef, and then one
|
// 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
|
// 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).
|
// rustc does an autoderef and then autoref again).
|
||||||
|
|
||||||
let krate = resolver.krate()?;
|
let krate = resolver.krate()?;
|
||||||
|
// TODO no autoderef in LookupMode::Path
|
||||||
for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
|
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);
|
return Some(result);
|
||||||
}
|
}
|
||||||
if let 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);
|
return Some(result);
|
||||||
}
|
}
|
||||||
|
@ -207,7 +222,8 @@ fn iterate_trait_method_candidates<T>(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mode: LookupMode,
|
||||||
|
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let krate = resolver.krate()?;
|
let krate = resolver.krate()?;
|
||||||
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
|
// 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
|
// trait, but if we find out it doesn't, we'll skip the rest of the
|
||||||
// iteration
|
// iteration
|
||||||
let mut known_implemented = inherently_implemented;
|
let mut known_implemented = inherently_implemented;
|
||||||
for item in data.items() {
|
for &item in data.items() {
|
||||||
if let AssocItem::Function(m) = *item {
|
// TODO unify with the impl case
|
||||||
let data = m.data(db);
|
match item {
|
||||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
AssocItem::Function(m) => {
|
||||||
if !known_implemented {
|
let data = m.data(db);
|
||||||
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
|
if !name.map_or(true, |name| data.name() == name)
|
||||||
if db.trait_solve(krate, goal).is_none() {
|
|| (!data.has_self_param() && mode != LookupMode::Path)
|
||||||
continue 'traits;
|
{
|
||||||
}
|
continue;
|
||||||
}
|
|
||||||
known_implemented = true;
|
|
||||||
if let Some(result) = callback(&ty.value, m) {
|
|
||||||
return Some(result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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>,
|
ty: &Canonical<Ty>,
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
|
mode: LookupMode,
|
||||||
krate: Crate,
|
krate: Crate,
|
||||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
for krate in def_crates(db, krate, &ty.value)? {
|
for krate in def_crates(db, krate, &ty.value)? {
|
||||||
let impls = db.impls_in_crate(krate);
|
let impls = db.impls_in_crate(krate);
|
||||||
|
|
||||||
for impl_block in impls.lookup_impl_blocks(&ty.value) {
|
for impl_block in impls.lookup_impl_blocks(&ty.value) {
|
||||||
for item in impl_block.items(db) {
|
for item in impl_block.items(db) {
|
||||||
if let AssocItem::Function(f) = item {
|
match item {
|
||||||
let data = f.data(db);
|
AssocItem::Function(f) => {
|
||||||
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
|
let data = f.data(db);
|
||||||
if let Some(result) = callback(&ty.value, f) {
|
if !name.map_or(true, |name| data.name() == name)
|
||||||
return Some(result);
|
|| (!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
|
[243; 254) 'Struct::FOO': u32
|
||||||
[264; 265) 'y': u32
|
[264; 265) 'y': u32
|
||||||
[268; 277) 'Enum::BAR': u32
|
[268; 277) 'Enum::BAR': u32
|
||||||
[287; 288) 'z': {unknown}
|
[287; 288) 'z': u32
|
||||||
[291; 304) 'TraitTest::ID': {unknown}
|
[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) {
|
fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
|
||||||
let mut seen_methods = FxHashSet::default();
|
let mut seen_methods = FxHashSet::default();
|
||||||
ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| {
|
ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, item| {
|
||||||
let data = func.data(ctx.db);
|
if let hir::AssocItem::Function(func) = item {
|
||||||
if data.has_self_param() && seen_methods.insert(data.name().clone()) {
|
let data = func.data(ctx.db);
|
||||||
acc.add_function(ctx, func);
|
if data.has_self_param() && seen_methods.insert(data.name().clone()) {
|
||||||
|
acc.add_function(ctx, func);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None::<()>
|
None::<()>
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue