mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-02 11:44:28 +00:00
Lower fully qualified associated type paths
I.e. `<T as Trait>::Foo`.
This commit is contained in:
parent
6cfdfdecba
commit
22724f37f3
@ -71,6 +71,7 @@ fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
|
|||||||
ast::PathSegmentKind::SelfKw => a == "self",
|
ast::PathSegmentKind::SelfKw => a == "self",
|
||||||
ast::PathSegmentKind::SuperKw => a == "super",
|
ast::PathSegmentKind::SuperKw => a == "super",
|
||||||
ast::PathSegmentKind::CrateKw => a == "crate",
|
ast::PathSegmentKind::CrateKw => a == "crate",
|
||||||
|
ast::PathSegmentKind::Type { .. } => false, // not allowed in imports
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -25,6 +25,12 @@ pub struct PathSegment {
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct GenericArgs {
|
pub struct GenericArgs {
|
||||||
pub args: Vec<GenericArg>,
|
pub args: Vec<GenericArg>,
|
||||||
|
/// This specifies whether the args contain a Self type as the first
|
||||||
|
/// element. This is the case for path segments like `<T as Trait>`, where
|
||||||
|
/// `T` is actually a type parameter for the path `Trait` specifying the
|
||||||
|
/// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type
|
||||||
|
/// is left out.
|
||||||
|
pub has_self_type: bool,
|
||||||
// someday also bindings
|
// someday also bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +80,28 @@ impl Path {
|
|||||||
let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
|
let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
|
||||||
segments.push(segment);
|
segments.push(segment);
|
||||||
}
|
}
|
||||||
|
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
|
||||||
|
assert!(path.qualifier().is_none()); // this can only occur at the first segment
|
||||||
|
|
||||||
|
// FIXME: handle <T> syntax (type segments without trait)
|
||||||
|
|
||||||
|
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
|
||||||
|
let path = Path::from_ast(trait_ref?.path()?)?;
|
||||||
|
kind = path.kind;
|
||||||
|
let mut prefix_segments = path.segments;
|
||||||
|
prefix_segments.reverse();
|
||||||
|
segments.extend(prefix_segments);
|
||||||
|
// Insert the type reference (T in the above example) as Self parameter for the trait
|
||||||
|
let self_type = TypeRef::from_ast(type_ref?);
|
||||||
|
let mut last_segment = segments.last_mut()?;
|
||||||
|
if last_segment.args_and_bindings.is_none() {
|
||||||
|
last_segment.args_and_bindings = Some(Arc::new(GenericArgs::empty()));
|
||||||
|
};
|
||||||
|
let args = last_segment.args_and_bindings.as_mut().unwrap();
|
||||||
|
let mut args_inner = Arc::make_mut(args);
|
||||||
|
args_inner.has_self_type = true;
|
||||||
|
args_inner.args.insert(0, GenericArg::Type(self_type));
|
||||||
|
}
|
||||||
ast::PathSegmentKind::CrateKw => {
|
ast::PathSegmentKind::CrateKw => {
|
||||||
kind = PathKind::Crate;
|
kind = PathKind::Crate;
|
||||||
break;
|
break;
|
||||||
@ -144,11 +172,15 @@ impl GenericArgs {
|
|||||||
}
|
}
|
||||||
// lifetimes and assoc type args ignored for now
|
// lifetimes and assoc type args ignored for now
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
Some(GenericArgs { args })
|
Some(GenericArgs { args, has_self_type: false })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn empty() -> GenericArgs {
|
||||||
|
GenericArgs { args: Vec::new(), has_self_type: false }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Name> for Path {
|
impl From<Name> for Path {
|
||||||
@ -236,6 +268,10 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
|
|||||||
}
|
}
|
||||||
Path { kind: PathKind::Super, segments: Vec::new() }
|
Path { kind: PathKind::Super, segments: Vec::new() }
|
||||||
}
|
}
|
||||||
|
ast::PathSegmentKind::Type { .. } => {
|
||||||
|
// not allowed in imports
|
||||||
|
return None;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{FnSig, GenericPredicate, Substs, TraitRef, Ty, TypeCtor};
|
use super::{FnSig, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor};
|
||||||
use crate::{
|
use crate::{
|
||||||
adt::VariantDef,
|
adt::VariantDef,
|
||||||
generics::HasGenericParams,
|
generics::HasGenericParams,
|
||||||
@ -64,7 +64,8 @@ impl Ty {
|
|||||||
|
|
||||||
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self {
|
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self {
|
||||||
// Resolve the path (in type namespace)
|
// Resolve the path (in type namespace)
|
||||||
let resolution = resolver.resolve_path_without_assoc_items(db, path).take_types();
|
let (resolution, remaining_index) = resolver.resolve_path_segments(db, path).into_inner();
|
||||||
|
let resolution = resolution.take_types();
|
||||||
|
|
||||||
let def = match resolution {
|
let def = match resolution {
|
||||||
Some(Resolution::Def(def)) => def,
|
Some(Resolution::Def(def)) => def,
|
||||||
@ -73,6 +74,10 @@ impl Ty {
|
|||||||
panic!("path resolved to local binding in type ns");
|
panic!("path resolved to local binding in type ns");
|
||||||
}
|
}
|
||||||
Some(Resolution::GenericParam(idx)) => {
|
Some(Resolution::GenericParam(idx)) => {
|
||||||
|
if remaining_index.is_some() {
|
||||||
|
// e.g. T::Item
|
||||||
|
return Ty::Unknown;
|
||||||
|
}
|
||||||
return Ty::Param {
|
return Ty::Param {
|
||||||
idx,
|
idx,
|
||||||
// FIXME: maybe return name in resolution?
|
// FIXME: maybe return name in resolution?
|
||||||
@ -83,18 +88,59 @@ impl Ty {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
Some(Resolution::SelfType(impl_block)) => {
|
Some(Resolution::SelfType(impl_block)) => {
|
||||||
|
if remaining_index.is_some() {
|
||||||
|
// e.g. Self::Item
|
||||||
|
return Ty::Unknown;
|
||||||
|
}
|
||||||
return impl_block.target_ty(db);
|
return impl_block.target_ty(db);
|
||||||
}
|
}
|
||||||
None => return Ty::Unknown,
|
None => {
|
||||||
|
// path did not resolve
|
||||||
|
return Ty::Unknown;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let typable: TypableDef = match def.into() {
|
if let ModuleDef::Trait(trait_) = def {
|
||||||
None => return Ty::Unknown,
|
let segment = match remaining_index {
|
||||||
Some(it) => it,
|
None => path.segments.last().expect("resolved path has at least one element"),
|
||||||
};
|
Some(i) => &path.segments[i - 1],
|
||||||
let ty = db.type_for_def(typable, Namespace::Types);
|
};
|
||||||
let substs = Ty::substs_from_path(db, resolver, path, typable);
|
let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None);
|
||||||
ty.subst(&substs)
|
if let Some(remaining_index) = remaining_index {
|
||||||
|
if remaining_index == path.segments.len() - 1 {
|
||||||
|
let segment = &path.segments[remaining_index];
|
||||||
|
let associated_ty =
|
||||||
|
match trait_ref.trait_.associated_type_by_name(db, segment.name.clone()) {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
// associated type not found
|
||||||
|
return Ty::Unknown;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
eprintln!(
|
||||||
|
"assoc ty: {:?}, parameters: {:?}",
|
||||||
|
associated_ty.name(db),
|
||||||
|
trait_ref.substs
|
||||||
|
);
|
||||||
|
// FIXME handle type parameters on the segment
|
||||||
|
Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs })
|
||||||
|
} else {
|
||||||
|
// FIXME more than one segment remaining, is this possible?
|
||||||
|
Ty::Unknown
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// FIXME dyn Trait without the dyn
|
||||||
|
Ty::Unknown
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let typable: TypableDef = match def.into() {
|
||||||
|
None => return Ty::Unknown,
|
||||||
|
Some(it) => it,
|
||||||
|
};
|
||||||
|
let ty = db.type_for_def(typable, Namespace::Types);
|
||||||
|
let substs = Ty::substs_from_path(db, resolver, path, typable);
|
||||||
|
ty.subst(&substs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn substs_from_path_segment(
|
pub(super) fn substs_from_path_segment(
|
||||||
@ -219,14 +265,25 @@ impl TraitRef {
|
|||||||
Resolution::Def(ModuleDef::Trait(tr)) => tr,
|
Resolution::Def(ModuleDef::Trait(tr)) => tr,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let mut substs = Self::substs_from_path(db, resolver, path, resolved);
|
let segment = path.segments.last().expect("path should have at least one segment");
|
||||||
|
Some(TraitRef::from_resolved_path(db, resolver, resolved, segment, explicit_self_ty))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_resolved_path(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
resolver: &Resolver,
|
||||||
|
resolved: Trait,
|
||||||
|
segment: &PathSegment,
|
||||||
|
explicit_self_ty: Option<Ty>,
|
||||||
|
) -> Self {
|
||||||
|
let mut substs = TraitRef::substs_from_path(db, resolver, segment, resolved);
|
||||||
if let Some(self_ty) = explicit_self_ty {
|
if let Some(self_ty) = explicit_self_ty {
|
||||||
// FIXME this could be nicer
|
// FIXME this could be nicer
|
||||||
let mut substs_vec = substs.0.to_vec();
|
let mut substs_vec = substs.0.to_vec();
|
||||||
substs_vec[0] = self_ty;
|
substs_vec[0] = self_ty;
|
||||||
substs.0 = substs_vec.into();
|
substs.0 = substs_vec.into();
|
||||||
}
|
}
|
||||||
Some(TraitRef { trait_: resolved, substs })
|
TraitRef { trait_: resolved, substs }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_hir(
|
pub(crate) fn from_hir(
|
||||||
@ -245,11 +302,12 @@ impl TraitRef {
|
|||||||
fn substs_from_path(
|
fn substs_from_path(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
path: &Path,
|
segment: &PathSegment,
|
||||||
resolved: Trait,
|
resolved: Trait,
|
||||||
) -> Substs {
|
) -> Substs {
|
||||||
let segment = path.segments.last().expect("path should have at least one segment");
|
let has_self_param =
|
||||||
substs_from_path_segment(db, resolver, segment, Some(resolved.into()), true)
|
segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false);
|
||||||
|
substs_from_path_segment(db, resolver, segment, Some(resolved.into()), !has_self_param)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef {
|
pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef {
|
||||||
|
@ -2515,10 +2515,10 @@ fn test<T: Iterable>() {
|
|||||||
@r###"
|
@r###"
|
||||||
⋮
|
⋮
|
||||||
⋮[108; 227) '{ ...ter; }': ()
|
⋮[108; 227) '{ ...ter; }': ()
|
||||||
⋮[118; 119) 'x': i32
|
⋮[118; 119) 'x': <S as Iterable>::Item
|
||||||
⋮[145; 146) '1': i32
|
⋮[145; 146) '1': <S as Iterable>::Item
|
||||||
⋮[156; 157) 'y': {unknown}
|
⋮[156; 157) 'y': <T as Iterable>::Item
|
||||||
⋮[183; 192) 'no_matter': {unknown}
|
⋮[183; 192) 'no_matter': <T as Iterable>::Item
|
||||||
⋮[202; 203) 'z': {unknown}
|
⋮[202; 203) 'z': {unknown}
|
||||||
⋮[215; 224) 'no_matter': {unknown}
|
⋮[215; 224) 'no_matter': {unknown}
|
||||||
"###
|
"###
|
||||||
@ -2552,9 +2552,9 @@ fn test() {
|
|||||||
⋮[205; 209) 'foo1': fn foo1<S>(T) -> {unknown}
|
⋮[205; 209) 'foo1': fn foo1<S>(T) -> {unknown}
|
||||||
⋮[205; 212) 'foo1(S)': {unknown}
|
⋮[205; 212) 'foo1(S)': {unknown}
|
||||||
⋮[210; 211) 'S': S
|
⋮[210; 211) 'S': S
|
||||||
⋮[222; 223) 'y': {unknown}
|
⋮[222; 223) 'y': <S as Iterable>::Item
|
||||||
⋮[226; 230) 'foo2': fn foo2<S>(T) -> {unknown}
|
⋮[226; 230) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item
|
||||||
⋮[226; 233) 'foo2(S)': {unknown}
|
⋮[226; 233) 'foo2(S)': <S as Iterable>::Item
|
||||||
⋮[231; 232) 'S': S
|
⋮[231; 232) 'S': S
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
@ -91,6 +91,7 @@ impl ast::Attr {
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum PathSegmentKind {
|
pub enum PathSegmentKind {
|
||||||
Name(ast::NameRef),
|
Name(ast::NameRef),
|
||||||
|
Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> },
|
||||||
SelfKw,
|
SelfKw,
|
||||||
SuperKw,
|
SuperKw,
|
||||||
CrateKw,
|
CrateKw,
|
||||||
@ -112,6 +113,15 @@ impl ast::PathSegment {
|
|||||||
T![self] => PathSegmentKind::SelfKw,
|
T![self] => PathSegmentKind::SelfKw,
|
||||||
T![super] => PathSegmentKind::SuperKw,
|
T![super] => PathSegmentKind::SuperKw,
|
||||||
T![crate] => PathSegmentKind::CrateKw,
|
T![crate] => PathSegmentKind::CrateKw,
|
||||||
|
T![<] => {
|
||||||
|
// <T> or <T as Trait>
|
||||||
|
// T is any TypeRef, Trait has to be a PathType
|
||||||
|
let mut type_refs =
|
||||||
|
self.syntax().children().filter(|node| ast::TypeRef::can_cast(node.kind()));
|
||||||
|
let type_ref = type_refs.next().and_then(ast::TypeRef::cast);
|
||||||
|
let trait_ref = type_refs.next().and_then(ast::PathType::cast);
|
||||||
|
PathSegmentKind::Type { type_ref, trait_ref }
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user