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 e827f277b1e..b61f052a1e0 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -423,8 +423,13 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () { check_assist( add_missing_default_members, r#" +struct Bar { + bar: [i32, N] +} + trait Foo { fn get_n_sq(&self, arg: &T) -> usize { N * N } + fn get_array(&self, arg: Bar) -> [i32; N] { [1; N] } } struct S { @@ -435,8 +440,13 @@ impl Foo for S { $0 }"#, r#" +struct Bar { + bar: [i32, N] +} + trait Foo { fn get_n_sq(&self, arg: &T) -> usize { N * N } + fn get_array(&self, arg: Bar) -> [i32; N] { [1; N] } } struct S { @@ -445,6 +455,31 @@ struct S { impl Foo for S { $0fn get_n_sq(&self, arg: &Z) -> usize { X * X } + + fn get_array(&self, arg: Bar) -> [i32; X] { [1; X] } +}"#, + ) + } + + #[test] + fn test_const_substitution_2() { + check_assist( + add_missing_default_members, + r#" +trait Foo { + fn get_sum(&self, arg: &T) -> usize { N + M } +} + +impl Foo<42, {20 + 22}, X> for () { + $0 +}"#, + r#" +trait Foo { + fn get_sum(&self, arg: &T) -> usize { N + M } +} + +impl Foo<42, {20 + 22}, X> for () { + $0fn get_sum(&self, arg: &X) -> usize { 42 + {20 + 22} } }"#, ) } diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 28d815e81b4..797180fa189 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -958,7 +958,6 @@ fn main() { ); } - // FIXME: const generics aren't being substituted, this is blocked on better support for them #[test] fn inline_substitutes_generics() { check_assist( @@ -982,7 +981,7 @@ fn foo() { fn bar() {} fn main() { - bar::(); + bar::(); } "#, ); diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index d280185c4ce..73e6a920ee4 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -11,12 +11,15 @@ use syntax::{ #[derive(Default)] struct AstSubsts { - // ast::TypeArgs stands in fact for both type and const params - // as consts declared elsewhere look just like type params. - types_and_consts: Vec, + types_and_consts: Vec, lifetimes: Vec, } +enum TypeOrConst { + Either(ast::TypeArg), // indistinguishable type or const param + Const(ast::ConstArg), +} + type LifetimeName = String; /// `PathTransform` substitutes path in SyntaxNodes in bulk. @@ -125,27 +128,38 @@ impl<'a> PathTransform<'a> { // the resulting change can be applied correctly. .zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None))) .for_each(|(k, v)| match (k.split(db), v) { - (Either::Right(t), Some(v)) => { + (Either::Right(k), Some(TypeOrConst::Either(v))) => { if let Some(ty) = v.ty() { - type_substs.insert(t, ty.clone()); + type_substs.insert(k, ty.clone()); } } - (Either::Right(t), None) => { - if let Some(default) = t.default(db) { + (Either::Right(k), None) => { + if let Some(default) = k.default(db) { if let Some(default) = &default.display_source_code(db, source_module.into(), false).ok() { - type_substs.insert(t, ast::make::ty(default).clone_for_update()); - default_types.push(t); + type_substs.insert(k, ast::make::ty(default).clone_for_update()); + default_types.push(k); } } } - (Either::Left(c), Some(v)) => { + (Either::Left(k), Some(TypeOrConst::Either(v))) => { if let Some(ty) = v.ty() { - const_substs.insert(c, ty.syntax().clone()); + const_substs.insert(k, ty.syntax().clone()); + } + } + (Either::Left(k), Some(TypeOrConst::Const(v))) => { + if let Some(expr) = v.expr() { + // FIXME: expressions in curly brackets can cause ambiguity after insertion + // (e.g. `N * 2` -> `{1 + 1} * 2`; it's unclear whether `{1 + 1}` + // is a standalone statement or a part of another expresson) + // and sometimes require slight modifications; see + // https://doc.rust-lang.org/reference/statements.html#expression-statements + const_substs.insert(k, expr.syntax().clone()); } } (Either::Left(_), None) => (), // FIXME: get default const value + _ => (), // ignore mismatching params }); let lifetime_substs: FxHashMap<_, _> = self .generic_def @@ -201,6 +215,9 @@ impl<'a> Ctx<'a> { fn transform_default_type_substs(&self, default_types: Vec) { for k in default_types { let v = self.type_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::>(); for path in paths { self.transform_path(path); @@ -326,8 +343,12 @@ fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option< // Const params are marked as consts on definition only, // being passed to the trait they are indistguishable from type params; // anyway, we don't really need to distinguish them here. - ast::GenericArg::TypeArg(type_or_const_arg) => { - result.types_and_consts.push(type_or_const_arg) + ast::GenericArg::TypeArg(type_arg) => { + result.types_and_consts.push(TypeOrConst::Either(type_arg)) + } + // Some const values are recognized correctly. + ast::GenericArg::ConstArg(const_arg) => { + result.types_and_consts.push(TypeOrConst::Const(const_arg)); } ast::GenericArg::LifetimeArg(l_arg) => result.lifetimes.push(l_arg), _ => (),