about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-ty/src/consteval.rs1
-rw-r--r--crates/hir-ty/src/infer.rs25
-rw-r--r--crates/hir-ty/src/infer/path.rs4
-rw-r--r--crates/hir-ty/src/method_resolution.rs42
-rw-r--r--crates/hir/src/source_analyzer.rs34
-rw-r--r--crates/ide/src/hover/tests.rs157
6 files changed, 245 insertions, 18 deletions
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 2c0c6e0b839..e4714f5ab9e 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -351,6 +351,7 @@ pub fn eval_const(
                         .infer
                         .assoc_resolutions_for_expr(expr_id)
                         .ok_or(ConstEvalError::SemanticError("unresolved assoc item"))?
+                        .0
                     {
                         hir_def::AssocItemId::FunctionId(_) => {
                             Err(ConstEvalError::NotSupported("assoc function"))
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 112eb5bd84c..ba78543f09e 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -330,7 +330,7 @@ pub struct InferenceResult {
     /// For each struct literal or pattern, records the variant it resolves to.
     variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
     /// For each associated item record what it resolves to
-    assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
+    assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, Option<Substitution>)>,
     pub diagnostics: Vec<InferenceDiagnostic>,
     pub type_of_expr: ArenaMap<ExprId, Ty>,
     /// For each pattern record the type it resolves to.
@@ -360,11 +360,17 @@ impl InferenceResult {
     pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> {
         self.variant_resolutions.get(&id.into()).copied()
     }
-    pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<AssocItemId> {
-        self.assoc_resolutions.get(&id.into()).copied()
+    pub fn assoc_resolutions_for_expr(
+        &self,
+        id: ExprId,
+    ) -> Option<(AssocItemId, Option<Substitution>)> {
+        self.assoc_resolutions.get(&id.into()).cloned()
     }
-    pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<AssocItemId> {
-        self.assoc_resolutions.get(&id.into()).copied()
+    pub fn assoc_resolutions_for_pat(
+        &self,
+        id: PatId,
+    ) -> Option<(AssocItemId, Option<Substitution>)> {
+        self.assoc_resolutions.get(&id.into()).cloned()
     }
     pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
         self.type_mismatches.get(&expr.into())
@@ -621,8 +627,13 @@ impl<'a> InferenceContext<'a> {
         self.result.variant_resolutions.insert(id, variant);
     }
 
-    fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId) {
-        self.result.assoc_resolutions.insert(id, item);
+    fn write_assoc_resolution(
+        &mut self,
+        id: ExprOrPatId,
+        item: AssocItemId,
+        subs: Option<Substitution>,
+    ) {
+        self.result.assoc_resolutions.insert(id, (item, subs));
     }
 
     fn write_pat_ty(&mut self, pat: PatId, ty: Ty) {
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index ebe9d6fb5e0..c1666c60e97 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -212,7 +212,7 @@ impl<'a> InferenceContext<'a> {
             AssocItemId::TypeAliasId(_) => unreachable!(),
         };
 
-        self.write_assoc_resolution(id, item);
+        self.write_assoc_resolution(id, item, Some(trait_ref.substitution.clone()));
         Some((def, Some(trait_ref.substitution)))
     }
 
@@ -273,7 +273,7 @@ impl<'a> InferenceContext<'a> {
                     ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
                 };
 
-                self.write_assoc_resolution(id, item);
+                self.write_assoc_resolution(id, item, substs.clone());
                 Some((def, substs))
             },
         )
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 0d980699ff5..882f1afc1f8 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -642,6 +642,30 @@ pub(crate) fn iterate_method_candidates<T>(
     slot
 }
 
+pub fn lookup_impl_const(
+    db: &dyn HirDatabase,
+    env: Arc<TraitEnvironment>,
+    const_id: ConstId,
+    subs: Substitution,
+) -> ConstId {
+    let trait_id = match const_id.lookup(db.upcast()).container {
+        ItemContainerId::TraitId(id) => id,
+        _ => return const_id,
+    };
+    let substitution = Substitution::from_iter(Interner, subs.iter(Interner));
+    let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution };
+
+    let const_data = db.const_data(const_id);
+    let name = match const_data.name.as_ref() {
+        Some(name) => name,
+        None => return const_id,
+    };
+
+    lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
+        .and_then(|assoc| if let AssocItemId::ConstId(id) = assoc { Some(id) } else { None })
+        .unwrap_or(const_id)
+}
+
 /// Looks up the impl method that actually runs for the trait method `func`.
 ///
 /// Returns `func` if it's not a method defined in a trait or the lookup failed.
@@ -663,15 +687,17 @@ pub fn lookup_impl_method(
     };
 
     let name = &db.function_data(func).name;
-    lookup_impl_method_for_trait_ref(trait_ref, db, env, name).unwrap_or(func)
+    lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
+        .and_then(|assoc| if let AssocItemId::FunctionId(id) = assoc { Some(id) } else { None })
+        .unwrap_or(func)
 }
 
-fn lookup_impl_method_for_trait_ref(
+fn lookup_impl_assoc_item_for_trait_ref(
     trait_ref: TraitRef,
     db: &dyn HirDatabase,
     env: Arc<TraitEnvironment>,
     name: &Name,
-) -> Option<FunctionId> {
+) -> Option<AssocItemId> {
     let self_ty = trait_ref.self_type_parameter(Interner);
     let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
     let impls = db.trait_impls_in_deps(env.krate);
@@ -681,7 +707,15 @@ fn lookup_impl_method_for_trait_ref(
 
     let impl_data = find_matching_impl(impls, table, trait_ref)?;
     impl_data.items.iter().find_map(|it| match it {
-        AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
+        AssocItemId::FunctionId(f) => {
+            (db.function_data(*f).name == *name).then(|| AssocItemId::FunctionId(*f))
+        }
+        AssocItemId::ConstId(c) => db
+            .const_data(*c)
+            .name
+            .as_ref()
+            .map(|n| *n == *name)
+            .and_then(|result| if result { Some(AssocItemId::ConstId(*c)) } else { None }),
         _ => None,
     })
 }
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 91ea1c24d14..837aa1defe2 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -21,8 +21,8 @@ use hir_def::{
     path::{ModPath, Path, PathKind},
     resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
     type_ref::Mutability,
-    AsMacroCall, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId,
-    Lookup, ModuleDefId, TraitId, VariantId,
+    AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
+    LocalFieldId, Lookup, ModuleDefId, TraitId, VariantId,
 };
 use hir_expand::{
     builtin_fn_macro::BuiltinFnLikeExpander,
@@ -482,7 +482,7 @@ impl SourceAnalyzer {
             let infer = self.infer.as_deref()?;
             if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
                 let expr_id = self.expr_id(db, &path_expr.into())?;
-                if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) {
+                if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr(expr_id) {
                     let assoc = match assoc {
                         AssocItemId::FunctionId(f_in_trait) => {
                             match infer.type_of_expr.get(expr_id) {
@@ -501,7 +501,13 @@ impl SourceAnalyzer {
                                 }
                             }
                         }
-
+                        AssocItemId::ConstId(const_id) => {
+                            if let Some(subs) = subs {
+                                self.resolve_impl_const_or_trait_def(db, const_id, subs).into()
+                            } else {
+                                assoc
+                            }
+                        }
                         _ => assoc,
                     };
 
@@ -515,7 +521,7 @@ impl SourceAnalyzer {
                 prefer_value_ns = true;
             } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
                 let pat_id = self.pat_id(&path_pat.into())?;
-                if let Some(assoc) = infer.assoc_resolutions_for_pat(pat_id) {
+                if let Some((assoc, _)) = infer.assoc_resolutions_for_pat(pat_id) {
                     return Some(PathResolution::Def(AssocItem::from(assoc).into()));
                 }
                 if let Some(VariantId::EnumVariantId(variant)) =
@@ -792,6 +798,24 @@ impl SourceAnalyzer {
         method_resolution::lookup_impl_method(db, env, func, substs)
     }
 
+    fn resolve_impl_const_or_trait_def(
+        &self,
+        db: &dyn HirDatabase,
+        const_id: ConstId,
+        subs: Substitution,
+    ) -> ConstId {
+        let krate = self.resolver.krate();
+        let owner = match self.resolver.body_owner() {
+            Some(it) => it,
+            None => return const_id,
+        };
+        let env = owner.as_generic_def_id().map_or_else(
+            || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
+            |d| db.trait_environment(d),
+        );
+        method_resolution::lookup_impl_const(db, env, const_id, subs)
+    }
+
     fn lang_trait_fn(
         &self,
         db: &dyn HirDatabase,
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index f8be4cfb04c..9fb5dd8c332 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -3636,6 +3636,163 @@ enum E {
 
 #[test]
 fn hover_const_eval() {
+    check(
+        r#"
+trait T {
+    const B: bool = false;
+}
+impl T for <()> {
+    /// true
+    const B: bool = true;
+}
+fn main() {
+    <()>::B$0;
+}
+"#,
+        expect![[r#"
+        *B*
+
+        ```rust
+        test
+        ```
+
+        ```rust
+        const B: bool = true
+        ```
+
+        ---
+
+        true
+    "#]],
+    );
+
+    check(
+        r#"
+struct A {
+    i: i32
+};
+
+trait T {
+    const AA: A = A {
+        i: 1
+    };
+}
+impl T for i32 {
+    const AA: A = A {
+        i: 2
+    }
+}
+fn main() {
+    <i32>::AA$0;
+}
+"#,
+        expect![[r#"
+        *AA*
+
+        ```rust
+        test
+        ```
+
+        ```rust
+        const AA: A = A {
+                i: 2
+            }
+        ```
+    "#]],
+    );
+
+    check(
+        r#"
+trait T {
+    /// false
+    const B: bool = false;
+}
+impl T for () {
+    /// true
+    const B: bool = true;
+}
+fn main() {
+    T::B$0;
+}
+"#,
+        expect![[r#"
+            *B*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const B: bool = false
+            ```
+
+            ---
+
+            false
+        "#]],
+    );
+
+    check(
+        r#"
+trait T {
+    /// false
+    const B: bool = false;
+}
+impl T for () {
+}
+fn main() {
+    <()>::B$0;
+}
+"#,
+        expect![[r#"
+            *B*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const B: bool = false
+            ```
+
+            ---
+
+            false
+        "#]],
+    );
+
+    check(
+        r#"
+trait T {
+    /// false
+    const B: bool = false;
+}
+impl T for () {
+    /// true
+    const B: bool = true;
+}
+impl T for i32 {}
+fn main() {
+    <i32>::B$0;
+}
+"#,
+        expect![[r#"
+            *B*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const B: bool = false
+            ```
+
+            ---
+
+            false
+        "#]],
+    );
+
     // show hex for <10
     check(
         r#"