about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-ty/src/lower.rs6
-rw-r--r--crates/ide-assists/src/handlers/add_missing_impl_members.rs31
-rw-r--r--crates/ide-db/src/path_transform.rs23
3 files changed, 49 insertions, 11 deletions
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 419a50ebe5f..a885c32e7f9 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -1612,12 +1612,12 @@ pub(crate) fn generic_defaults_query(
                         let unknown = unknown_const_as_generic(
                             db.const_param_ty(ConstParamId::from_unchecked(id)),
                         );
-                        let val = p.default.as_ref().map_or(unknown, |c| {
+                        let mut val = p.default.as_ref().map_or(unknown, |c| {
                             let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
                             chalk_ir::GenericArg::new(Interner, GenericArgData::Const(c))
                         });
-                        // FIXME: check if complex default values refer to
-                        // previous parameters they should not.
+                        // Each default can only refer to previous parameters, see above.
+                        val = fallback_bound_vars(val, idx, parent_start_idx);
                         return make_binders(db, &generic_params, val);
                     }
                 };
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index ea659a2295d..d9faf3a7f6c 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -519,6 +519,37 @@ impl<X> Foo<X> for () {
     }
 
     #[test]
+    fn test_const_substitution_with_defaults_2() {
+        check_assist(
+            add_missing_impl_members,
+            r#"
+mod m {
+    pub const LEN: usize = 42;
+    pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
+        fn get_t(&self) -> T;
+    }
+}
+
+impl m::Foo for () {
+    $0
+}"#,
+            r#"
+mod m {
+    pub const LEN: usize = 42;
+    pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
+        fn get_t(&self) -> T;
+    }
+}
+
+impl m::Foo for () {
+    fn get_t(&self) -> [bool; m::LEN] {
+        ${0:todo!()}
+    }
+}"#,
+        )
+    }
+
+    #[test]
     fn test_cursor_after_empty_impl_def() {
         check_assist(
             add_missing_impl_members,
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 507200ea3ba..efe13f06040 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -21,6 +21,7 @@ enum TypeOrConst {
 }
 
 type LifetimeName = String;
+type DefaultedParam = Either<hir::TypeParam, hir::ConstParam>;
 
 /// `PathTransform` substitutes path in SyntaxNodes in bulk.
 ///
@@ -115,7 +116,7 @@ impl<'a> PathTransform<'a> {
         };
         let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
         let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
-        let mut default_types: Vec<hir::TypeParam> = Default::default();
+        let mut defaulted_params: Vec<DefaultedParam> = Default::default();
         self.generic_def
             .into_iter()
             .flat_map(|it| it.type_params(db))
@@ -139,7 +140,7 @@ impl<'a> PathTransform<'a> {
                             &default.display_source_code(db, source_module.into(), false).ok()
                         {
                             type_substs.insert(k, ast::make::ty(default).clone_for_update());
-                            default_types.push(k);
+                            defaulted_params.push(Either::Left(k));
                         }
                     }
                 }
@@ -162,7 +163,7 @@ impl<'a> PathTransform<'a> {
                     if let Some(default) = k.default(db) {
                         if let Some(default) = ast::make::expr_const_value(&default).expr() {
                             const_substs.insert(k, default.syntax().clone_for_update());
-                            // FIXME: transform the default value
+                            defaulted_params.push(Either::Right(k));
                         }
                     }
                 }
@@ -182,7 +183,7 @@ impl<'a> PathTransform<'a> {
             target_module,
             source_scope: self.source_scope,
         };
-        ctx.transform_default_type_substs(default_types);
+        ctx.transform_default_values(defaulted_params);
         ctx
     }
 }
@@ -219,13 +220,19 @@ impl Ctx<'_> {
         });
     }
 
-    fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) {
-        for k in default_types {
-            let v = self.type_substs.get(&k).unwrap();
+    fn transform_default_values(&self, defaulted_params: Vec<DefaultedParam>) {
+        // By now the default values are simply copied from where they are declared
+        // and should be transformed. As any value is allowed to refer to previous
+        // generic (both type and const) parameters, they should be all iterated left-to-right.
+        for param in defaulted_params {
+            let value = match param {
+                Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(),
+                Either::Right(k) => self.const_substs.get(&k).unwrap(),
+            };
             // `transform_path` may update a node's parent and that would break the
             // tree traversal. Thus all paths in the tree are collected into a vec
             // so that such operation is safe.
-            let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
+            let paths = postorder(value).filter_map(ast::Path::cast).collect::<Vec<_>>();
             for path in paths {
                 self.transform_path(path);
             }