Refactor some more

Type-relative paths (`<T>::foo`) also need to work in type context, for example
`<T>::Item` is legal. So rather than returning the type ref from the resolver
function, just check it before.
This commit is contained in:
Florian Diebold 2019-09-16 21:38:27 +02:00
parent 406280e52f
commit fe1dfd2b20
4 changed files with 100 additions and 57 deletions

View File

@ -190,6 +190,13 @@ impl Path {
pub fn expand_macro_expr(&self) -> Option<Name> {
self.as_ident().and_then(|name| Some(name.clone()))
}
pub fn is_type_relative(&self) -> bool {
match self.kind {
PathKind::Type(_) => true,
_ => false,
}
}
}
impl GenericArgs {

View File

@ -15,7 +15,6 @@ use crate::{
name::{Name, SELF_PARAM, SELF_TYPE},
nameres::{CrateDefMap, CrateModuleId, PerNs},
path::{Path, PathKind},
type_ref::TypeRef,
Adt, BuiltinType, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct,
Trait, TypeAlias,
};
@ -65,10 +64,9 @@ pub enum TypeNs {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ResolveValueResult<'a> {
pub enum ResolveValueResult {
ValueNs(ValueNs),
Partial(TypeNs, usize),
TypeRef(&'a TypeRef),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -131,6 +129,9 @@ impl Resolver {
db: &impl HirDatabase,
path: &Path,
) -> Option<(TypeNs, Option<usize>)> {
if path.is_type_relative() {
return None;
}
let first_name = &path.segments.first()?.name;
let skip_to_mod = path.kind != PathKind::Plain;
for scope in self.scopes.iter().rev() {
@ -189,11 +190,10 @@ impl Resolver {
&self,
db: &impl HirDatabase,
path: &'p Path,
) -> Option<ResolveValueResult<'p>> {
if let PathKind::Type(type_ref) = &path.kind {
return Some(ResolveValueResult::TypeRef(type_ref));
) -> Option<ResolveValueResult> {
if path.is_type_relative() {
return None;
}
let n_segments = path.segments.len();
let tmp = SELF_PARAM;
let first_name = if path.is_self() { &tmp } else { &path.segments.first()?.name };
@ -284,7 +284,7 @@ impl Resolver {
) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? {
ResolveValueResult::ValueNs(it) => Some(it),
ResolveValueResult::Partial(..) | ResolveValueResult::TypeRef(_) => None,
ResolveValueResult::Partial(..) => None,
}
}

View File

@ -48,8 +48,7 @@ use crate::{
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
ty::infer::diagnostics::InferenceDiagnostic,
type_ref::{Mutability, TypeRef},
Adt, AssocItem, ConstData, DefWithBody, Either, FnData, Function, HasBody, Name, Path,
StructField,
Adt, AssocItem, ConstData, DefWithBody, FnData, Function, HasBody, Name, Path, StructField,
};
mod unify;
@ -468,16 +467,27 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId) -> Option<Ty> {
let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?;
let (value, self_subst) = match value_or_partial {
ResolveValueResult::ValueNs(it) => (it, None),
ResolveValueResult::Partial(def, remaining_index) => {
self.resolve_assoc_item(Either::A(def), path, remaining_index, id)?
let (value, self_subst) = if let crate::PathKind::Type(type_ref) = &path.kind {
if path.segments.is_empty() {
// This can't actually happen syntax-wise
return None;
}
ResolveValueResult::TypeRef(type_ref) => {
let ty = self.make_ty(type_ref);
self.resolve_assoc_item(Either::B(ty), path, 0, id)?
let ty = self.make_ty(type_ref);
let remaining_segments_for_ty = &path.segments[..path.segments.len() - 1];
let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty);
self.resolve_ty_assoc_item(
ty,
path.segments.last().expect("path had at least one segment"),
id,
)?
} else {
let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?;
match value_or_partial {
ResolveValueResult::ValueNs(it) => (it, None),
ResolveValueResult::Partial(def, remaining_index) => {
self.resolve_assoc_item(def, path, remaining_index, id)?
}
}
};
@ -508,15 +518,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
fn resolve_assoc_item(
&mut self,
// mut def_or_ty: Either<TypeNs, Ty>,
def: TypeNs,
path: &Path,
remaining_index: usize,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<Substs>)> {
assert!(remaining_index < path.segments.len());
let krate = self.resolver.krate()?;
// there may be more intermediate segments between the resolved one and
// the end. Only the last segment needs to be resolved to a value; from
// the segments before that, we need to get either a type or a trait ref.
@ -525,11 +532,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let remaining_segments = &path.segments[remaining_index..];
let is_before_last = remaining_segments.len() == 1;
let (def, substs) = match (def, is_before_last) {
match (def, is_before_last) {
(TypeNs::Trait(_trait), true) => {
// Associated item of trait, e.g. `Default::default`
// FIXME
return None;
// FIXME Associated item of trait, e.g. `Default::default`
None
}
(def, _) => {
// Either we already have a type (e.g. `Vec::new`), or we have a
@ -550,29 +556,45 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
let segment =
remaining_segments.last().expect("there should be at least one segment here");
// Find impl
let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item {
crate::ImplItem::Method(func) => {
if segment.name == func.name(self.db) {
Some(ValueNs::Function(func))
} else {
None
}
}
crate::ImplItem::Const(konst) => {
if segment.name == konst.name(self.db) {
Some(ValueNs::Const(konst))
} else {
None
}
}
crate::ImplItem::TypeAlias(_) => None,
})?;
let self_types = self.find_self_types(&def, ty);
(def, self_types)
self.resolve_ty_assoc_item(ty, segment, id)
}
};
}
}
fn resolve_ty_assoc_item(
&mut self,
ty: Ty,
segment: &crate::path::PathSegment,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<Substs>)> {
if let Ty::Unknown = ty {
return None;
}
let krate = self.resolver.krate()?;
// Find impl
// FIXME: consider trait candidates
let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item {
crate::ImplItem::Method(func) => {
if segment.name == func.name(self.db) {
Some(ValueNs::Function(func))
} else {
None
}
}
crate::ImplItem::Const(konst) => {
if konst.name(self.db).map_or(false, |n| n == segment.name) {
Some(ValueNs::Const(konst))
} else {
None
}
}
crate::ImplItem::TypeAlias(_) => None,
})?;
let substs = self.find_self_types(&def, ty);
self.write_assoc_resolution(
id,

View File

@ -86,6 +86,24 @@ impl Ty {
}
}
pub(crate) fn from_type_relative_path(
db: &impl HirDatabase,
resolver: &Resolver,
ty: Ty,
remaining_segments: &[PathSegment],
) -> Ty {
if remaining_segments.len() == 1 {
// resolve unselected assoc types
let segment = &remaining_segments[0];
Ty::select_associated_type(db, resolver, ty, segment)
} else if remaining_segments.len() > 1 {
// FIXME report error (ambiguous associated type)
Ty::Unknown
} else {
ty
}
}
pub(crate) fn from_partly_resolved_hir_path(
db: &impl HirDatabase,
resolver: &Resolver,
@ -140,20 +158,16 @@ impl Ty {
TypeNs::EnumVariant(_) => return Ty::Unknown,
};
if remaining_segments.len() == 1 {
// resolve unselected assoc types
let segment = &remaining_segments[0];
Ty::select_associated_type(db, resolver, ty, segment)
} else if remaining_segments.len() > 1 {
// FIXME report error (ambiguous associated type)
Ty::Unknown
} else {
ty
}
Ty::from_type_relative_path(db, resolver, ty, remaining_segments)
}
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty {
// Resolve the path (in type namespace)
if let crate::PathKind::Type(type_ref) = &path.kind {
let ty = Ty::from_hir(db, resolver, &type_ref);
let remaining_segments = &path.segments[..];
return Ty::from_type_relative_path(db, resolver, ty, remaining_segments);
}
let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) {
Some(it) => it,
None => return Ty::Unknown,