mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Refactor associated method resolution a bit and make it work with generics
This commit is contained in:
parent
1eef9fbefe
commit
82fe7b77a3
4 changed files with 98 additions and 64 deletions
|
@ -360,46 +360,66 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||
// we might have resolved into a type for which
|
||||
// we may find some associated item starting at the
|
||||
// path.segment pointed to by `remaining_index´
|
||||
let resolved =
|
||||
let mut resolved =
|
||||
if remaining_index.is_none() { def.take_values()? } else { def.take_types()? };
|
||||
|
||||
let remaining_index = remaining_index.unwrap_or(path.segments.len());
|
||||
|
||||
// resolve intermediate segments
|
||||
for segment in &path.segments[remaining_index..] {
|
||||
let ty = match resolved {
|
||||
Resolution::Def(def) => {
|
||||
let typable: Option<TypableDef> = def.into();
|
||||
let typable = typable?;
|
||||
|
||||
let substs =
|
||||
Ty::substs_from_path_segment(self.db, &self.resolver, segment, typable);
|
||||
self.db.type_for_def(typable, Namespace::Types).apply_substs(substs)
|
||||
}
|
||||
Resolution::LocalBinding(_) => {
|
||||
// can't have a local binding in an associated item path
|
||||
return None;
|
||||
}
|
||||
Resolution::GenericParam(..) => {
|
||||
// TODO associated item of generic param
|
||||
return None;
|
||||
}
|
||||
Resolution::SelfType(_) => {
|
||||
// TODO associated item of self type
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Attempt to find an impl_item for the type which has a name matching
|
||||
// the current segment
|
||||
log::debug!("looking for path segment: {:?}", segment);
|
||||
let item = ty.iterate_impl_items(self.db, |item| match item {
|
||||
crate::ImplItem::Method(func) => {
|
||||
let sig = func.signature(self.db);
|
||||
if segment.name == *sig.name() {
|
||||
return Some(func);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// TODO: Resolve associated const
|
||||
crate::ImplItem::Const(_) => None,
|
||||
|
||||
// TODO: Resolve associated types
|
||||
crate::ImplItem::Type(_) => None,
|
||||
})?;
|
||||
resolved = Resolution::Def(item.into());
|
||||
}
|
||||
|
||||
match resolved {
|
||||
Resolution::Def(def) => {
|
||||
let typable: Option<TypableDef> = def.into();
|
||||
let typable = typable?;
|
||||
|
||||
if let Some(remaining_index) = remaining_index {
|
||||
let ty = self.db.type_for_def(typable, Namespace::Types);
|
||||
// TODO: Keep resolving the segments
|
||||
// if we have more segments to process
|
||||
let segment = &path.segments[remaining_index];
|
||||
|
||||
log::debug!("looking for path segment: {:?}", segment);
|
||||
|
||||
// Attempt to find an impl_item for the type which has a name matching
|
||||
// the current segment
|
||||
let ty = ty.iterate_impl_items(self.db, |item| match item {
|
||||
crate::ImplItem::Method(func) => {
|
||||
let sig = func.signature(self.db);
|
||||
if segment.name == *sig.name() {
|
||||
return Some(func.ty(self.db));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// TODO: Resolve associated const
|
||||
crate::ImplItem::Const(_) => None,
|
||||
|
||||
// TODO: Resolve associated types
|
||||
crate::ImplItem::Type(_) => None,
|
||||
});
|
||||
ty
|
||||
} else {
|
||||
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
|
||||
let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs);
|
||||
let ty = self.insert_type_vars(ty);
|
||||
Some(ty)
|
||||
}
|
||||
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
|
||||
let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs);
|
||||
let ty = self.insert_type_vars(ty);
|
||||
Some(ty)
|
||||
}
|
||||
Resolution::LocalBinding(pat) => {
|
||||
let ty = self.type_of_pat.get(pat)?;
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
name::KnownName,
|
||||
nameres::Namespace,
|
||||
resolve::{Resolver, Resolution},
|
||||
path::GenericArg,
|
||||
path::{ PathSegment, GenericArg},
|
||||
generics::GenericParams,
|
||||
adt::VariantDef,
|
||||
};
|
||||
|
@ -112,36 +112,18 @@ impl Ty {
|
|||
ty.apply_substs(substs)
|
||||
}
|
||||
|
||||
/// Collect generic arguments from a path into a `Substs`. See also
|
||||
/// `create_substs_for_ast_path` and `def_to_ty` in rustc.
|
||||
pub(super) fn substs_from_path(
|
||||
pub(super) fn substs_from_path_segment(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
path: &Path,
|
||||
segment: &PathSegment,
|
||||
resolved: TypableDef,
|
||||
) -> Substs {
|
||||
let mut substs = Vec::new();
|
||||
let last = path.segments.last().expect("path should have at least one segment");
|
||||
let (def_generics, segment) = match resolved {
|
||||
TypableDef::Function(func) => (func.generic_params(db), last),
|
||||
TypableDef::Struct(s) => (s.generic_params(db), last),
|
||||
TypableDef::Enum(e) => (e.generic_params(db), last),
|
||||
TypableDef::EnumVariant(var) => {
|
||||
// the generic args for an enum variant may be either specified
|
||||
// on the segment referring to the enum, or on the segment
|
||||
// referring to the variant. So `Option::<T>::None` and
|
||||
// `Option::None::<T>` are both allowed (though the former is
|
||||
// preferred). See also `def_ids_for_path_segments` in rustc.
|
||||
let len = path.segments.len();
|
||||
let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() {
|
||||
// Option::<T>::None
|
||||
&path.segments[len - 2]
|
||||
} else {
|
||||
// Option::None::<T>
|
||||
last
|
||||
};
|
||||
(var.parent_enum(db).generic_params(db), segment)
|
||||
}
|
||||
let def_generics = match resolved {
|
||||
TypableDef::Function(func) => func.generic_params(db),
|
||||
TypableDef::Struct(s) => s.generic_params(db),
|
||||
TypableDef::Enum(e) => e.generic_params(db),
|
||||
TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db),
|
||||
};
|
||||
let parent_param_count = def_generics.count_parent_params();
|
||||
substs.extend((0..parent_param_count).map(|_| Ty::Unknown));
|
||||
|
@ -166,6 +148,39 @@ impl Ty {
|
|||
assert_eq!(substs.len(), def_generics.count_params_including_parent());
|
||||
Substs(substs.into())
|
||||
}
|
||||
|
||||
/// Collect generic arguments from a path into a `Substs`. See also
|
||||
/// `create_substs_for_ast_path` and `def_to_ty` in rustc.
|
||||
pub(super) fn substs_from_path(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
path: &Path,
|
||||
resolved: TypableDef,
|
||||
) -> Substs {
|
||||
let last = path.segments.last().expect("path should have at least one segment");
|
||||
let segment = match resolved {
|
||||
TypableDef::Function(_) => last,
|
||||
TypableDef::Struct(_) => last,
|
||||
TypableDef::Enum(_) => last,
|
||||
TypableDef::EnumVariant(_) => {
|
||||
// the generic args for an enum variant may be either specified
|
||||
// on the segment referring to the enum, or on the segment
|
||||
// referring to the variant. So `Option::<T>::None` and
|
||||
// `Option::None::<T>` are both allowed (though the former is
|
||||
// preferred). See also `def_ids_for_path_segments` in rustc.
|
||||
let len = path.segments.len();
|
||||
let segment = if len >= 2 && path.segments[len - 2].args_and_bindings.is_some() {
|
||||
// Option::<T>::None
|
||||
&path.segments[len - 2]
|
||||
} else {
|
||||
// Option::None::<T>
|
||||
last
|
||||
};
|
||||
segment
|
||||
}
|
||||
};
|
||||
Ty::substs_from_path_segment(db, resolver, segment, resolved)
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the declared type of an item. This depends on the namespace; e.g. for
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
created: "2019-02-21T10:25:18.568887300Z"
|
||||
created: "2019-02-23T21:58:35.844769207Z"
|
||||
creator: insta@0.6.3
|
||||
source: crates/ra_hir/src/ty/tests.rs
|
||||
expression: "&result"
|
||||
|
@ -9,8 +9,8 @@ expression: "&result"
|
|||
[92; 103) 'Gen { val }': Gen<T>
|
||||
[98; 101) 'val': T
|
||||
[123; 155) '{ ...32); }': ()
|
||||
[133; 134) 'a': Gen<[unknown]>
|
||||
[137; 146) 'Gen::make': fn make<[unknown]>(T) -> Gen<T>
|
||||
[137; 152) 'Gen::make(0u32)': Gen<[unknown]>
|
||||
[133; 134) 'a': Gen<u32>
|
||||
[137; 146) 'Gen::make': fn make<u32>(T) -> Gen<T>
|
||||
[137; 152) 'Gen::make(0u32)': Gen<u32>
|
||||
[147; 151) '0u32': u32
|
||||
|
||||
|
|
|
@ -719,7 +719,6 @@ fn test() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // FIXME: After https://github.com/rust-analyzer/rust-analyzer/pull/866 is merged
|
||||
fn infer_associated_method_generics() {
|
||||
check_inference(
|
||||
"infer_associated_method_generics",
|
||||
|
|
Loading…
Reference in a new issue