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.rs363
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!()
+ }
+}
+"#,
+ );
+ }
}