Skip to content

Commit 266e028

Browse files
committed
the "implement missing members" assist's const transformation implemented
1 parent 6b3659d commit 266e028

File tree

2 files changed

+66
-21
lines changed

2 files changed

+66
-21
lines changed

crates/ide-assists/src/handlers/add_missing_impl_members.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,37 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
400400
);
401401
}
402402

403+
#[test]
404+
fn test_const_substitution() {
405+
check_assist(
406+
add_missing_default_members,
407+
r#"
408+
trait Foo<const N: usize, T> {
409+
fn get_n_sq(&self, arg: &T) -> usize { N * N }
410+
}
411+
412+
struct S<T> {
413+
wrapped: T
414+
}
415+
416+
impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
417+
$0
418+
}"#,
419+
r#"
420+
trait Foo<const N: usize, T> {
421+
fn get_n_sq(&self, arg: &T) -> usize { N * N }
422+
}
423+
424+
struct S<T> {
425+
wrapped: T
426+
}
427+
428+
impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
429+
$0fn get_n_sq(&self, arg: &Z) -> usize { X * X }
430+
}"#,
431+
)
432+
}
433+
403434
#[test]
404435
fn test_cursor_after_empty_impl_def() {
405436
check_assist(

crates/ide-db/src/path_transform.rs

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use syntax::{
1111

1212
#[derive(Default)]
1313
struct AstSubsts {
14-
types: Vec<ast::TypeArg>,
14+
// ast::TypeArgs stands in fact for both type and const params
15+
// as consts declared elsewhere look just like type params.
16+
types_and_consts: Vec<ast::TypeArg>,
1517
lifetimes: Vec<ast::LifetimeArg>,
1618
}
1719

@@ -108,7 +110,7 @@ impl<'a> PathTransform<'a> {
108110
Some(hir::GenericDef::Trait(_)) => 1,
109111
_ => 0,
110112
};
111-
let type_substs: FxHashMap<_, _> = self
113+
let type_and_const_substs: FxHashMap<_, _> = self
112114
.generic_def
113115
.into_iter()
114116
.flat_map(|it| it.type_params(db))
@@ -119,19 +121,17 @@ impl<'a> PathTransform<'a> {
119121
// can still hit those trailing values and check if they actually have
120122
// a default type. If they do, go for that type from `hir` to `ast` so
121123
// the resulting change can be applied correctly.
122-
.zip(self.substs.types.iter().map(Some).chain(std::iter::repeat(None)))
123-
.filter_map(|(k, v)| match k.split(db) {
124-
Either::Left(_) => None, // FIXME: map const types too
125-
Either::Right(t) => match v {
126-
Some(v) => Some((k, v.ty()?.clone())),
127-
None => {
128-
let default = t.default(db)?;
129-
let v = ast::make::ty(
130-
&default.display_source_code(db, source_module.into(), false).ok()?,
131-
);
132-
Some((k, v))
133-
}
134-
},
124+
.zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None)))
125+
.filter_map(|(k, v)| match (k.split(db), v) {
126+
(_, Some(v)) => Some((k, v.ty()?.clone())),
127+
(Either::Right(t), None) => {
128+
let default = t.default(db)?;
129+
let v = ast::make::ty(
130+
&default.display_source_code(db, source_module.into(), false).ok()?,
131+
);
132+
Some((k, v))
133+
}
134+
(Either::Left(_), None) => None, // FIXME: get default const value
135135
})
136136
.collect();
137137
let lifetime_substs: FxHashMap<_, _> = self
@@ -141,12 +141,17 @@ impl<'a> PathTransform<'a> {
141141
.zip(self.substs.lifetimes.clone())
142142
.filter_map(|(k, v)| Some((k.name(db).display(db.upcast()).to_string(), v.lifetime()?)))
143143
.collect();
144-
Ctx { type_substs, lifetime_substs, target_module, source_scope: self.source_scope }
144+
Ctx {
145+
type_and_const_substs,
146+
lifetime_substs,
147+
target_module,
148+
source_scope: self.source_scope,
149+
}
145150
}
146151
}
147152

148153
struct Ctx<'a> {
149-
type_substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
154+
type_and_const_substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
150155
lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
151156
target_module: hir::Module,
152157
source_scope: &'a SemanticsScope<'a>,
@@ -203,7 +208,7 @@ impl<'a> Ctx<'a> {
203208

204209
match resolution {
205210
hir::PathResolution::TypeParam(tp) => {
206-
if let Some(subst) = self.type_substs.get(&tp.merge()) {
211+
if let Some(subst) = self.type_and_const_substs.get(&tp.merge()) {
207212
let parent = path.syntax().parent()?;
208213
if let Some(parent) = ast::Path::cast(parent.clone()) {
209214
// Path inside path means that there is an associated
@@ -270,8 +275,12 @@ impl<'a> Ctx<'a> {
270275
}
271276
ted::replace(path.syntax(), res.syntax())
272277
}
278+
hir::PathResolution::ConstParam(cp) => {
279+
if let Some(subst) = self.type_and_const_substs.get(&cp.merge()) {
280+
ted::replace(path.syntax(), subst.clone_subtree().clone_for_update().syntax());
281+
}
282+
}
273283
hir::PathResolution::Local(_)
274-
| hir::PathResolution::ConstParam(_)
275284
| hir::PathResolution::SelfType(_)
276285
| hir::PathResolution::Def(_)
277286
| hir::PathResolution::BuiltinAttr(_)
@@ -298,9 +307,14 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<AstSubsts> {
298307
fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<AstSubsts> {
299308
let mut result = AstSubsts::default();
300309
generic_arg_list.generic_args().for_each(|generic_arg| match generic_arg {
301-
ast::GenericArg::TypeArg(type_arg) => result.types.push(type_arg),
310+
// Const params are marked as consts on definition only,
311+
// being passed to the trait they are indistguishable from type params;
312+
// anyway, we don't really need to distinguish them here.
313+
ast::GenericArg::TypeArg(type_or_const_arg) => {
314+
result.types_and_consts.push(type_or_const_arg)
315+
}
302316
ast::GenericArg::LifetimeArg(l_arg) => result.lifetimes.push(l_arg),
303-
_ => (), // FIXME: don't filter out const params
317+
_ => (),
304318
});
305319

306320
Some(result)

0 commit comments

Comments
 (0)