From 22724f37f3ae73983bf700d10d80a8dbd4fa4073 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 5 Aug 2019 22:42:38 +0200 Subject: [PATCH] Lower fully qualified associated type paths I.e. `::Foo`. --- crates/ra_assists/src/auto_import.rs | 1 + crates/ra_hir/src/path.rs | 38 ++++++++++- crates/ra_hir/src/ty/lower.rs | 88 +++++++++++++++++++++----- crates/ra_hir/src/ty/tests.rs | 14 ++-- crates/ra_syntax/src/ast/extensions.rs | 10 +++ 5 files changed, 128 insertions(+), 23 deletions(-) diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs index a32e2f9b610..1158adbbc0f 100644 --- a/crates/ra_assists/src/auto_import.rs +++ b/crates/ra_assists/src/auto_import.rs @@ -71,6 +71,7 @@ fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool { ast::PathSegmentKind::SelfKw => a == "self", ast::PathSegmentKind::SuperKw => a == "super", ast::PathSegmentKind::CrateKw => a == "crate", + ast::PathSegmentKind::Type { .. } => false, // not allowed in imports } } else { false diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs index 882db768162..5ee71e421e3 100644 --- a/crates/ra_hir/src/path.rs +++ b/crates/ra_hir/src/path.rs @@ -25,6 +25,12 @@ pub struct PathSegment { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericArgs { pub args: Vec, + /// This specifies whether the args contain a Self type as the first + /// element. This is the case for path segments like ``, where + /// `T` is actually a type parameter for the path `Trait` specifying the + /// Self type. Otherwise, when we have a path `Trait`, the Self type + /// is left out. + pub has_self_type: bool, // someday also bindings } @@ -74,6 +80,28 @@ impl Path { let segment = PathSegment { name: name.as_name(), args_and_bindings: args }; 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 syntax (type segments without trait) + + // >::Foo desugars to Trait::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 => { kind = PathKind::Crate; break; @@ -144,11 +172,15 @@ impl GenericArgs { } // lifetimes and assoc type args ignored for now if !args.is_empty() { - Some(GenericArgs { args }) + Some(GenericArgs { args, has_self_type: false }) } else { None } } + + pub(crate) fn empty() -> GenericArgs { + GenericArgs { args: Vec::new(), has_self_type: false } + } } impl From for Path { @@ -236,6 +268,10 @@ fn convert_path(prefix: Option, path: ast::Path) -> Option { } Path { kind: PathKind::Super, segments: Vec::new() } } + ast::PathSegmentKind::Type { .. } => { + // not allowed in imports + return None; + } }; Some(res) } diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 894ba06955a..24ec77fcf5c 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -8,7 +8,7 @@ use std::iter; use std::sync::Arc; -use super::{FnSig, GenericPredicate, Substs, TraitRef, Ty, TypeCtor}; +use super::{FnSig, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor}; use crate::{ adt::VariantDef, generics::HasGenericParams, @@ -64,7 +64,8 @@ impl Ty { pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self { // 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 { Some(Resolution::Def(def)) => def, @@ -73,6 +74,10 @@ impl Ty { panic!("path resolved to local binding in type ns"); } Some(Resolution::GenericParam(idx)) => { + if remaining_index.is_some() { + // e.g. T::Item + return Ty::Unknown; + } return Ty::Param { idx, // FIXME: maybe return name in resolution? @@ -83,18 +88,59 @@ impl Ty { }; } Some(Resolution::SelfType(impl_block)) => { + if remaining_index.is_some() { + // e.g. Self::Item + return Ty::Unknown; + } return impl_block.target_ty(db); } - None => return Ty::Unknown, + None => { + // path did not resolve + return Ty::Unknown; + } }; - 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) + if let ModuleDef::Trait(trait_) = def { + let segment = match remaining_index { + None => path.segments.last().expect("resolved path has at least one element"), + Some(i) => &path.segments[i - 1], + }; + let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None); + 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( @@ -219,14 +265,25 @@ impl TraitRef { Resolution::Def(ModuleDef::Trait(tr)) => tr, _ => 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, + ) -> Self { + let mut substs = TraitRef::substs_from_path(db, resolver, segment, resolved); if let Some(self_ty) = explicit_self_ty { // FIXME this could be nicer let mut substs_vec = substs.0.to_vec(); substs_vec[0] = self_ty; substs.0 = substs_vec.into(); } - Some(TraitRef { trait_: resolved, substs }) + TraitRef { trait_: resolved, substs } } pub(crate) fn from_hir( @@ -245,11 +302,12 @@ impl TraitRef { fn substs_from_path( db: &impl HirDatabase, resolver: &Resolver, - path: &Path, + segment: &PathSegment, resolved: Trait, ) -> Substs { - let segment = path.segments.last().expect("path should have at least one segment"); - substs_from_path_segment(db, resolver, segment, Some(resolved.into()), true) + let has_self_param = + 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 { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 9d412ff613a..01b35833595 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2515,10 +2515,10 @@ fn test() { @r###" ⋮ ⋮[108; 227) '{ ...ter; }': () - ⋮[118; 119) 'x': i32 - ⋮[145; 146) '1': i32 - ⋮[156; 157) 'y': {unknown} - ⋮[183; 192) 'no_matter': {unknown} + ⋮[118; 119) 'x': ::Item + ⋮[145; 146) '1': ::Item + ⋮[156; 157) 'y': ::Item + ⋮[183; 192) 'no_matter': ::Item ⋮[202; 203) 'z': {unknown} ⋮[215; 224) 'no_matter': {unknown} "### @@ -2552,9 +2552,9 @@ fn test() { ⋮[205; 209) 'foo1': fn foo1(T) -> {unknown} ⋮[205; 212) 'foo1(S)': {unknown} ⋮[210; 211) 'S': S - ⋮[222; 223) 'y': {unknown} - ⋮[226; 230) 'foo2': fn foo2(T) -> {unknown} - ⋮[226; 233) 'foo2(S)': {unknown} + ⋮[222; 223) 'y': ::Item + ⋮[226; 230) 'foo2': fn foo2(T) -> ::Item + ⋮[226; 233) 'foo2(S)': ::Item ⋮[231; 232) 'S': S "### ); diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index d4873b39a81..2a59cf65373 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -91,6 +91,7 @@ impl ast::Attr { #[derive(Debug, Clone, PartialEq, Eq)] pub enum PathSegmentKind { Name(ast::NameRef), + Type { type_ref: Option, trait_ref: Option }, SelfKw, SuperKw, CrateKw, @@ -112,6 +113,15 @@ impl ast::PathSegment { T![self] => PathSegmentKind::SelfKw, T![super] => PathSegmentKind::SuperKw, T![crate] => PathSegmentKind::CrateKw, + T![<] => { + // or + // 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, } };