Repository: rust-analyzer/rust-analyzer
Pull request #1914 (2019-09-25 19:50:03)

Resolve trait associated items
E.g. `Default::default` or `<Foo as Default>::default`.


Patch:
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 9fecba63d1..20413cb3df 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1055,3 +1055,13 @@ pub enum AssocItem {
 // require not implementing From, and instead having some checked way of
 // casting them, and somehow making the constructors private, which would be annoying.
 impl_froms!(AssocItem: Function, Const, TypeAlias);
+
+impl From<AssocItem> for crate::generics::GenericDef {
+    fn from(item: AssocItem) -> Self {
+        match item {
+            AssocItem::Function(f) => f.into(),
+            AssocItem::Const(c) => c.into(),
+            AssocItem::TypeAlias(t) => t.into(),
+        }
+    }
+}
diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs
index ccb7774920..6865d34ba7 100644
--- a/crates/ra_hir/src/generics.rs
+++ b/crates/ra_hir/src/generics.rs
@@ -12,8 +12,8 @@ use crate::{
     name::SELF_TYPE,
     path::Path,
     type_ref::{TypeBound, TypeRef},
-    Adt, AsName, Container, Enum, EnumVariant, Function, HasSource, ImplBlock, Name, Struct, Trait,
-    TypeAlias, Union,
+    Adt, AsName, Const, Container, Enum, EnumVariant, Function, HasSource, ImplBlock, Name, Struct,
+    Trait, TypeAlias, Union,
 };
 
 /// Data about a generic parameter (to a function, struct, impl, ...).
@@ -44,7 +44,6 @@ pub struct WherePredicate {
     pub(crate) bound: TypeBound,
 }
 
-// FIXME: consts can have type parameters from their parents (i.e. associated consts of traits)
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 pub enum GenericDef {
     Function(Function),
@@ -55,6 +54,8 @@ pub enum GenericDef {
     // enum variants cannot have generics themselves, but their parent enums
     // can, and this makes some code easier to write
     EnumVariant(EnumVariant),
+    // consts can have type parameters from their parents (i.e. associated consts of traits)
+    Const(Const),
 }
 impl_froms!(
     GenericDef: Function,
@@ -62,7 +63,8 @@ impl_froms!(
     Trait,
     TypeAlias,
     ImplBlock,
-    EnumVariant
+    EnumVariant,
+    Const
 );
 
 impl GenericParams {
@@ -75,7 +77,7 @@ impl GenericParams {
             GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from),
             GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()),
             GenericDef::Adt(_) | GenericDef::Trait(_) => None,
-            GenericDef::ImplBlock(_) => None,
+            GenericDef::ImplBlock(_) | GenericDef::Const(_) => None,
         };
         let mut generics = GenericParams {
             def,
@@ -104,7 +106,7 @@ impl GenericParams {
             // type-parameter, but rather is a type-alias for impl's target
             // type, so this is handled by the resolver.
             GenericDef::ImplBlock(it) => generics.fill(&it.source(db).ast, start),
-            GenericDef::EnumVariant(_) => {}
+            GenericDef::EnumVariant(_) | GenericDef::Const(_) => {}
         }
 
         Arc::new(generics)
@@ -198,6 +200,7 @@ impl GenericDef {
             GenericDef::TypeAlias(inner) => inner.resolver(db),
             GenericDef::ImplBlock(inner) => inner.resolver(db),
             GenericDef::EnumVariant(inner) => inner.parent_enum(db).resolver(db),
+            GenericDef::Const(inner) => inner.resolver(db),
         }
     }
 }
diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs
index 54aae4f0ca..feb7481b2e 100644
--- a/crates/ra_hir/src/ty/infer/path.rs
+++ b/crates/ra_hir/src/ty/infer/path.rs
@@ -1,6 +1,6 @@
 //! Path expression resolution.
 
-use super::{ExprOrPatId, InferenceContext};
+use super::{ExprOrPatId, InferenceContext, TraitRef};
 use crate::{
     db::HirDatabase,
     resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
@@ -91,9 +91,17 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         let is_before_last = remaining_segments.len() == 1;
 
         match (def, is_before_last) {
-            (TypeNs::Trait(_trait), true) => {
-                // FIXME Associated item of trait, e.g. `Default::default`
-                None
+            (TypeNs::Trait(trait_), true) => {
+                let segment =
+                    remaining_segments.last().expect("there should be at least one segment here");
+                let trait_ref = TraitRef::from_resolved_path(
+                    self.db,
+                    &self.resolver,
+                    trait_,
+                    resolved_segment,
+                    None,
+                );
+                self.resolve_trait_assoc_item(trait_ref, segment, id)
             }
             (def, _) => {
                 // Either we already have a type (e.g. `Vec::new`), or we have a
@@ -120,6 +128,45 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         }
     }
 
+    fn resolve_trait_assoc_item(
+        &mut self,
+        trait_ref: TraitRef,
+        segment: &crate::path::PathSegment,
+        id: ExprOrPatId,
+    ) -> Option<(ValueNs, Option<Substs>)> {
+        let trait_ = trait_ref.trait_;
+        let item = trait_.items(self.db).iter().copied().find_map(|item| match item {
+            AssocItem::Function(func) => {
+                if segment.name == func.name(self.db) {
+                    Some(AssocItem::Function(func))
+                } else {
+                    None
+                }
+            }
+
+            AssocItem::Const(konst) => {
+                if konst.name(self.db).map_or(false, |n| n == segment.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 generics = item.generic_params(self.db);
+        let mut substs = Vec::with_capacity(generics.count_params_including_parent());
+        substs.extend(trait_ref.substs.iter().cloned());
+        substs.extend(std::iter::repeat(Ty::Unknown).take(generics.params.len()));
+
+        self.write_assoc_resolution(id, item);
+        Some((def, Some(substs.into())))
+    }
+
     fn resolve_ty_assoc_item(
         &mut self,
         ty: Ty,
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs
index 8d71abc95d..dd503d7719 100644
--- a/crates/ra_hir/src/ty/lower.rs
+++ b/crates/ra_hir/src/ty/lower.rs
@@ -386,7 +386,7 @@ impl TraitRef {
         Some(TraitRef::from_resolved_path(db, resolver, resolved, segment, explicit_self_ty))
     }
 
-    fn from_resolved_path(
+    pub(super) fn from_resolved_path(
         db: &impl HirDatabase,
         resolver: &Resolver,
         resolved: Trait,


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 2872cd27ba..f4f63ca934 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -2535,17 +2535,16 @@ fn test() {
 }
 "#),
         @r###"
-
     [87; 193) '{     ...t(); }': ()
     [97; 99) 's1': S
-    [105; 121) 'Defaul...efault': {unknown}
+    [105; 121) 'Defaul...efault': fn default<S>() -> Self
     [105; 123) 'Defaul...ault()': S
     [133; 135) 's2': {unknown}
     [138; 148) 'S::default': {unknown}
     [138; 150) 'S::default()': {unknown}
-    [160; 162) 's3': {unknown}
-    [165; 188) '<S as ...efault': {unknown}
-    [165; 190) '<S as ...ault()': {unknown}
+    [160; 162) 's3': S
+    [165; 188) '<S as ...efault': fn default<S>() -> Self
+    [165; 190) '<S as ...ault()': S
     "###
     );
 }
@@ -2674,9 +2673,9 @@ fn test() {
     [148; 149) 'y': u64
     [157; 158) 'S': S
     [157; 165) 'S.into()': u64
-    [175; 176) 'z': {unknown}
-    [179; 196) 'Into::...::into': {unknown}
-    [179; 199) 'Into::...nto(S)': {unknown}
+    [175; 176) 'z': u64
+    [179; 196) 'Into::...::into': fn into<S, u64>(Self) -> T
+    [179; 199) 'Into::...nto(S)': u64
     [197; 198) 'S': S
     "###
     );