Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/add_missing_impl_members.rs')
| -rw-r--r-- | crates/ide-assists/src/handlers/add_missing_impl_members.rs | 363 |
1 files changed, 323 insertions, 40 deletions
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 6340feda45..d07c637262 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -1,13 +1,9 @@ use hir::HasSource; -use ide_db::syntax_helpers::insert_whitespace_into_node::insert_ws_into; use syntax::ast::{self, make, AstNode}; use crate::{ assist_context::{AssistContext, Assists}, - utils::{ - add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, render_snippet, - Cursor, DefaultMethods, - }, + utils::{add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, DefaultMethods}, AssistId, AssistKind, }; @@ -130,50 +126,36 @@ fn add_missing_impl_members_inner( } let target = impl_def.syntax().text_range(); - acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { - let missing_items = missing_items - .into_iter() - .map(|it| { - if ctx.sema.hir_file_for(it.syntax()).is_macro() { - if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) { - return it; - } - } - it.clone_for_update() - }) - .collect(); - let (new_impl_def, first_new_item) = add_trait_assoc_items_to_impl( + acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |edit| { + let new_impl_def = edit.make_mut(impl_def.clone()); + let first_new_item = add_trait_assoc_items_to_impl( &ctx.sema, - missing_items, + &missing_items, trait_, - impl_def.clone(), + &new_impl_def, target_scope, ); - match ctx.config.snippet_cap { - None => builder.replace(target, new_impl_def.to_string()), - Some(cap) => { - let mut cursor = Cursor::Before(first_new_item.syntax()); - let placeholder; - if let DefaultMethods::No = mode { - if let ast::AssocItem::Fn(func) = &first_new_item { - if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() { - if let Some(m) = - func.syntax().descendants().find_map(ast::MacroCall::cast) - { - if m.syntax().text() == "todo!()" { - placeholder = m; - cursor = Cursor::Replace(placeholder.syntax()); - } + + if let Some(cap) = ctx.config.snippet_cap { + let mut placeholder = None; + if let DefaultMethods::No = mode { + if let ast::AssocItem::Fn(func) = &first_new_item { + if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() { + if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) + { + if m.syntax().text() == "todo!()" { + placeholder = Some(m); } } } } - builder.replace_snippet( - cap, - target, - render_snippet(cap, new_impl_def.syntax(), cursor), - ) } + + if let Some(macro_call) = placeholder { + edit.add_placeholder_snippet(cap, macro_call); + } else { + edit.add_tabstop_before(cap, first_new_item); + }; }; }) } @@ -366,6 +348,125 @@ impl<U> Foo<U> for S { } #[test] + fn test_lifetime_substitution() { + check_assist( + add_missing_impl_members, + r#" +pub trait Trait<'a, 'b, A, B, C> { + fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C; +} + +impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {$0}"#, + r#" +pub trait Trait<'a, 'b, A, B, C> { + fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C; +} + +impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () { + fn foo(&self, one: &'x T, anoter: &'y V) -> &'x U { + ${0:todo!()} + } +}"#, + ); + } + + #[test] + fn test_lifetime_substitution_with_body() { + check_assist( + add_missing_default_members, + r#" +pub trait Trait<'a, 'b, A, B, C: Default> { + fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) { + let value: &'a i32 = &0; + (C::default(), value) + } +} + +impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {$0}"#, + r#" +pub trait Trait<'a, 'b, A, B, C: Default> { + fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) { + let value: &'a i32 = &0; + (C::default(), value) + } +} + +impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () { + $0fn foo(&self, _one: &'x T, _anoter: &'y V) -> (U, &'x i32) { + let value: &'x i32 = &0; + (<U>::default(), value) + } +}"#, + ); + } + + #[test] + fn test_const_substitution() { + check_assist( + add_missing_default_members, + r#" +struct Bar<const: N: bool> { + bar: [i32, N] +} + +trait Foo<const N: usize, T> { + fn get_n_sq(&self, arg: &T) -> usize { N * N } + fn get_array(&self, arg: Bar<N>) -> [i32; N] { [1; N] } +} + +struct S<T> { + wrapped: T +} + +impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> { + $0 +}"#, + r#" +struct Bar<const: N: bool> { + bar: [i32, N] +} + +trait Foo<const N: usize, T> { + fn get_n_sq(&self, arg: &T) -> usize { N * N } + fn get_array(&self, arg: Bar<N>) -> [i32; N] { [1; N] } +} + +struct S<T> { + wrapped: T +} + +impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> { + $0fn get_n_sq(&self, arg: &Z) -> usize { X * X } + + fn get_array(&self, arg: Bar<X>) -> [i32; X] { [1; X] } +}"#, + ) + } + + #[test] + fn test_const_substitution_2() { + check_assist( + add_missing_default_members, + r#" +trait Foo<const N: usize, const M: usize, T> { + fn get_sum(&self, arg: &T) -> usize { N + M } +} + +impl<X> Foo<42, {20 + 22}, X> for () { + $0 +}"#, + r#" +trait Foo<const N: usize, const M: usize, T> { + fn get_sum(&self, arg: &T) -> usize { N + M } +} + +impl<X> Foo<42, {20 + 22}, X> for () { + $0fn get_sum(&self, arg: &X) -> usize { 42 + {20 + 22} } +}"#, + ) + } + + #[test] fn test_cursor_after_empty_impl_def() { check_assist( add_missing_impl_members, @@ -747,6 +848,115 @@ impl Foo<T> for S<T> { } #[test] + fn test_qualify_generic_default_parameter() { + check_assist( + add_missing_impl_members, + r#" +mod m { + pub struct S; + pub trait Foo<T = S> { + fn bar(&self, other: &T); + } +} + +struct S; +impl m::Foo for S { $0 }"#, + r#" +mod m { + pub struct S; + pub trait Foo<T = S> { + fn bar(&self, other: &T); + } +} + +struct S; +impl m::Foo for S { + fn bar(&self, other: &m::S) { + ${0:todo!()} + } +}"#, + ) + } + + #[test] + fn test_qualify_generic_default_parameter_2() { + check_assist( + add_missing_impl_members, + r#" +mod m { + pub struct Wrapper<T, V> { + one: T, + another: V + }; + pub struct S; + pub trait Foo<T = Wrapper<S, bool>> { + fn bar(&self, other: &T); + } +} + +struct S; +impl m::Foo for S { $0 }"#, + r#" +mod m { + pub struct Wrapper<T, V> { + one: T, + another: V + }; + pub struct S; + pub trait Foo<T = Wrapper<S, bool>> { + fn bar(&self, other: &T); + } +} + +struct S; +impl m::Foo for S { + fn bar(&self, other: &m::Wrapper<m::S, bool>) { + ${0:todo!()} + } +}"#, + ); + } + + #[test] + fn test_qualify_generic_default_parameter_3() { + check_assist( + add_missing_impl_members, + r#" +mod m { + pub struct Wrapper<T, V> { + one: T, + another: V + }; + pub struct S; + pub trait Foo<T = S, V = Wrapper<T, S>> { + fn bar(&self, other: &V); + } +} + +struct S; +impl m::Foo for S { $0 }"#, + r#" +mod m { + pub struct Wrapper<T, V> { + one: T, + another: V + }; + pub struct S; + pub trait Foo<T = S, V = Wrapper<T, S>> { + fn bar(&self, other: &V); + } +} + +struct S; +impl m::Foo for S { + fn bar(&self, other: &m::Wrapper<m::S, m::S>) { + ${0:todo!()} + } +}"#, + ); + } + + #[test] fn test_assoc_type_bounds_are_removed() { check_assist( add_missing_impl_members, @@ -1683,4 +1893,77 @@ impl m::Foo for S { }"#, ) } + + #[test] + fn nested_macro_should_not_cause_crash() { + check_assist( + add_missing_impl_members, + r#" +macro_rules! ty { () => { i32 } } +trait SomeTrait { type Output; } +impl SomeTrait for i32 { type Output = i64; } +macro_rules! define_method { + () => { + fn method(&mut self, params: <ty!() as SomeTrait>::Output); + }; +} +trait AnotherTrait { define_method!(); } +impl $0AnotherTrait for () { +} +"#, + r#" +macro_rules! ty { () => { i32 } } +trait SomeTrait { type Output; } +impl SomeTrait for i32 { type Output = i64; } +macro_rules! define_method { + () => { + fn method(&mut self, params: <ty!() as SomeTrait>::Output); + }; +} +trait AnotherTrait { define_method!(); } +impl AnotherTrait for () { + $0fn method(&mut self,params: <ty!()as SomeTrait>::Output) { + todo!() + } +} +"#, + ); + } + + // FIXME: `T` in `ty!(T)` should be replaced by `PathTransform`. + #[test] + fn paths_in_nested_macro_should_get_transformed() { + check_assist( + add_missing_impl_members, + r#" +macro_rules! ty { ($me:ty) => { $me } } +trait SomeTrait { type Output; } +impl SomeTrait for i32 { type Output = i64; } +macro_rules! define_method { + ($t:ty) => { + fn method(&mut self, params: <ty!($t) as SomeTrait>::Output); + }; +} +trait AnotherTrait<T: SomeTrait> { define_method!(T); } +impl $0AnotherTrait<i32> for () { +} +"#, + r#" +macro_rules! ty { ($me:ty) => { $me } } +trait SomeTrait { type Output; } +impl SomeTrait for i32 { type Output = i64; } +macro_rules! define_method { + ($t:ty) => { + fn method(&mut self, params: <ty!($t) as SomeTrait>::Output); + }; +} +trait AnotherTrait<T: SomeTrait> { define_method!(T); } +impl AnotherTrait<i32> for () { + $0fn method(&mut self,params: <ty!(T)as SomeTrait>::Output) { + todo!() + } +} +"#, + ); + } } |