From fe3170dc344f73126cd4ff2f197d49a8b7f2fe1f Mon Sep 17 00:00:00 2001 From: Aleksei Sidorov Date: Thu, 3 Sep 2020 01:32:18 +0300 Subject: Initial implementation of the #5085 issue --- .../handlers/replace_impl_trait_with_generic.rs | 62 ++++++++++++++++++++++ crates/assists/src/lib.rs | 2 + 2 files changed, 64 insertions(+) create mode 100644 crates/assists/src/handlers/replace_impl_trait_with_generic.rs (limited to 'crates/assists') diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs new file mode 100644 index 000000000..8af2d16dd --- /dev/null +++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs @@ -0,0 +1,62 @@ +use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: replace_impl_trait_with_generic +// +// Replaces `impl Trait` function argument with the named generic. +pub(crate) fn replace_impl_trait_with_generic( + acc: &mut Assists, + ctx: &AssistContext, +) -> Option<()> { + let type_impl_trait = ctx.find_node_at_offset::()?; + let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; + let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?; + + let generic_param_list = + type_fn.generic_param_list().unwrap_or_else(|| make::generic_param_list(None)); + + let impl_trait_ty = type_impl_trait + .syntax() + .descendants() + .last() + .and_then(ast::NameRef::cast)? + .text() + .to_string(); + + let target = type_fn.syntax().text_range(); + acc.add( + AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), + "Replace impl trait with generic", + target, + |edit| { + let generic_letter = impl_trait_ty[..1].to_string(); + edit.replace_ast::(type_impl_trait.into(), make::ty(&generic_letter)); + + let new_params = generic_param_list + .append_param(make::generic_param(generic_letter, Some(impl_trait_ty))); + let new_type_fn = type_fn.replace_descendant(generic_param_list, new_params); + edit.replace_ast(type_fn.clone(), new_type_fn); + }, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::check_assist; + + #[test] + fn replace_with_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: B) {} + "#, + ); + } +} diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 2e0d191a6..cbac53e71 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs @@ -155,6 +155,7 @@ mod handlers { mod remove_unused_param; mod reorder_fields; mod replace_if_let_with_match; + mod replace_impl_trait_with_generic; mod replace_let_with_if_let; mod replace_qualified_name_with_use; mod replace_unwrap_with_match; @@ -202,6 +203,7 @@ mod handlers { remove_unused_param::remove_unused_param, reorder_fields::reorder_fields, replace_if_let_with_match::replace_if_let_with_match, + replace_impl_trait_with_generic::replace_impl_trait_with_generic, replace_let_with_if_let::replace_let_with_if_let, replace_qualified_name_with_use::replace_qualified_name_with_use, replace_unwrap_with_match::replace_unwrap_with_match, -- cgit v1.2.3 From 7259cc82f362bd1cd00f47ff3ffc71be769162b6 Mon Sep 17 00:00:00 2001 From: Aleksei Sidorov Date: Thu, 3 Sep 2020 14:46:28 +0300 Subject: Resolve most of corner cases --- .../handlers/replace_impl_trait_with_generic.rs | 110 +++++++++++++++++++-- 1 file changed, 102 insertions(+), 8 deletions(-) (limited to 'crates/assists') diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs index 8af2d16dd..5b0d5d971 100644 --- a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs @@ -13,9 +13,6 @@ pub(crate) fn replace_impl_trait_with_generic( let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?; - let generic_param_list = - type_fn.generic_param_list().unwrap_or_else(|| make::generic_param_list(None)); - let impl_trait_ty = type_impl_trait .syntax() .descendants() @@ -31,11 +28,16 @@ pub(crate) fn replace_impl_trait_with_generic( target, |edit| { let generic_letter = impl_trait_ty[..1].to_string(); - edit.replace_ast::(type_impl_trait.into(), make::ty(&generic_letter)); - let new_params = generic_param_list - .append_param(make::generic_param(generic_letter, Some(impl_trait_ty))); - let new_type_fn = type_fn.replace_descendant(generic_param_list, new_params); + let generic_param_list = type_fn + .generic_param_list() + .unwrap_or_else(|| make::generic_param_list(None)) + .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty))); + + let new_type_fn = type_fn + .replace_descendant::(type_impl_trait.into(), make::ty(&generic_letter)) + .with_generic_params(generic_param_list); + edit.replace_ast(type_fn.clone(), new_type_fn); }, ) @@ -48,7 +50,7 @@ mod tests { use crate::tests::check_assist; #[test] - fn replace_with_generic_params() { + fn replace_impl_trait_with_generic_params() { check_assist( replace_impl_trait_with_generic, r#" @@ -59,4 +61,96 @@ mod tests { "#, ); } + + #[test] + fn replace_impl_trait_without_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: B) {} + "#, + ); + } + + #[test] + fn replace_two_impl_trait_with_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo(foo: impl Foo, bar: <|>impl Bar) {} + "#, + r#" + fn foo(foo: impl Foo, bar: B) {} + "#, + ); + } + + #[test] + fn replace_impl_trait_with_empty_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo<>(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: B) {} + "#, + ); + } + + #[test] + fn replace_impl_trait_with_empty_multiline_generic_params() { + // FIXME: It would be more correct to place the generic parameter + // on the next line after the left angle. + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo< + >(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: B) {} + "#, + ); + } + + #[test] + #[ignore = "This case is very rare but there is no simple solutions to fix it."] + fn replace_impl_trait_with_exist_generic_letter() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: C) {} + "#, + ); + } + + #[test] + fn replace_impl_trait_with_multiline_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo< + G: Foo, + F, + H, + >(bar: <|>impl Bar) {} + "#, + r#" + fn foo< + G: Foo, + F, + H, + B: Bar, + >(bar: B) {} + "#, + ); + } } -- cgit v1.2.3 From 477fa75cfbdc923be366c75cb03b1e352f63c8e8 Mon Sep 17 00:00:00 2001 From: Aleksei Sidorov Date: Fri, 4 Sep 2020 15:24:36 +0300 Subject: Fix nitpicks --- .../src/handlers/replace_impl_trait_with_generic.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'crates/assists') diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs index 5b0d5d971..612c48466 100644 --- a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs @@ -5,13 +5,21 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: replace_impl_trait_with_generic // // Replaces `impl Trait` function argument with the named generic. +// +// ``` +// fn foo(bar: <|>impl Bar) {} +// ``` +// -> +// ``` +// fn foo(bar: B) {} +// ``` pub(crate) fn replace_impl_trait_with_generic( acc: &mut Assists, ctx: &AssistContext, ) -> Option<()> { let type_impl_trait = ctx.find_node_at_offset::()?; let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; - let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?; + let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; let impl_trait_ty = type_impl_trait .syntax() @@ -27,7 +35,7 @@ pub(crate) fn replace_impl_trait_with_generic( "Replace impl trait with generic", target, |edit| { - let generic_letter = impl_trait_ty[..1].to_string(); + let generic_letter = impl_trait_ty.chars().next().unwrap().to_string(); let generic_param_list = type_fn .generic_param_list() @@ -36,7 +44,7 @@ pub(crate) fn replace_impl_trait_with_generic( let new_type_fn = type_fn .replace_descendant::(type_impl_trait.into(), make::ty(&generic_letter)) - .with_generic_params(generic_param_list); + .with_generic_param_list(generic_param_list); edit.replace_ast(type_fn.clone(), new_type_fn); }, @@ -103,8 +111,6 @@ mod tests { #[test] fn replace_impl_trait_with_empty_multiline_generic_params() { - // FIXME: It would be more correct to place the generic parameter - // on the next line after the left angle. check_assist( replace_impl_trait_with_generic, r#" @@ -147,8 +153,7 @@ mod tests { fn foo< G: Foo, F, - H, - B: Bar, + H, B: Bar, >(bar: B) {} "#, ); -- cgit v1.2.3 From a1c060c8d0fa3d7f7ad27f56be8298e5f9759c95 Mon Sep 17 00:00:00 2001 From: Aleksei Sidorov Date: Fri, 4 Sep 2020 15:28:08 +0300 Subject: Update codegen --- crates/assists/src/tests/generated.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'crates/assists') diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 04c8fd1f9..3963136d8 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs @@ -814,6 +814,19 @@ fn handle(action: Action) { ) } +#[test] +fn doctest_replace_impl_trait_with_generic() { + check_doc_test( + "replace_impl_trait_with_generic", + r#####" +fn foo(bar: <|>impl Bar) {} +"#####, + r#####" +fn foo(bar: B) {} +"#####, + ) +} + #[test] fn doctest_replace_let_with_if_let() { check_doc_test( -- cgit v1.2.3 From ef0a1b2e58b6047223a68f700ef16fcc21f9f208 Mon Sep 17 00:00:00 2001 From: Aleksei Sidorov Date: Fri, 4 Sep 2020 17:55:27 +0300 Subject: Fix tests --- .../handlers/replace_impl_trait_with_generic.rs | 37 +++++++++++++--------- crates/assists/src/tests/generated.rs | 4 +-- 2 files changed, 24 insertions(+), 17 deletions(-) (limited to 'crates/assists') diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs index 612c48466..748c528d4 100644 --- a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs @@ -7,11 +7,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // Replaces `impl Trait` function argument with the named generic. // // ``` -// fn foo(bar: <|>impl Bar) {} +// fn foo(bar: <|>impl Bar) {} // ``` // -> // ``` -// fn foo(bar: B) {} +// fn foo(bar: B) {} // ``` pub(crate) fn replace_impl_trait_with_generic( acc: &mut Assists, @@ -21,13 +21,7 @@ pub(crate) fn replace_impl_trait_with_generic( let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; - let impl_trait_ty = type_impl_trait - .syntax() - .descendants() - .last() - .and_then(ast::NameRef::cast)? - .text() - .to_string(); + let impl_trait_ty = type_impl_trait.type_bound_list()?; let target = type_fn.syntax().text_range(); acc.add( @@ -35,7 +29,7 @@ pub(crate) fn replace_impl_trait_with_generic( "Replace impl trait with generic", target, |edit| { - let generic_letter = impl_trait_ty.chars().next().unwrap().to_string(); + let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string(); let generic_param_list = type_fn .generic_param_list() @@ -65,7 +59,7 @@ mod tests { fn foo(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: B) {} + fn foo(bar: B) {} "#, ); } @@ -78,7 +72,7 @@ mod tests { fn foo(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: B) {} + fn foo(bar: B) {} "#, ); } @@ -91,7 +85,7 @@ mod tests { fn foo(foo: impl Foo, bar: <|>impl Bar) {} "#, r#" - fn foo(foo: impl Foo, bar: B) {} + fn foo(foo: impl Foo, bar: B) {} "#, ); } @@ -104,7 +98,7 @@ mod tests { fn foo<>(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: B) {} + fn foo(bar: B) {} "#, ); } @@ -133,7 +127,7 @@ mod tests { fn foo(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: C) {} + fn foo(bar: C) {} "#, ); } @@ -158,4 +152,17 @@ mod tests { "#, ); } + + #[test] + fn replace_impl_trait_multiple() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo(bar: <|>impl Foo + Bar) {} + "#, + r#" + fn foo(bar: F) {} + "#, + ); + } } diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 3963136d8..4e5ca3825 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs @@ -819,10 +819,10 @@ fn doctest_replace_impl_trait_with_generic() { check_doc_test( "replace_impl_trait_with_generic", r#####" -fn foo(bar: <|>impl Bar) {} +fn foo(bar: <|>impl Bar) {} "#####, r#####" -fn foo(bar: B) {} +fn foo(bar: B) {} "#####, ) } -- cgit v1.2.3 From e1b8d836a9b64169a8337be674c78cac20940b92 Mon Sep 17 00:00:00 2001 From: Aleksei Sidorov Date: Fri, 4 Sep 2020 22:58:50 +0300 Subject: Remove unnecessary comma --- .../src/handlers/replace_impl_trait_with_generic.rs | 18 +++++++++--------- crates/assists/src/tests/generated.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'crates/assists') diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs index 748c528d4..6738bc134 100644 --- a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs @@ -11,7 +11,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` // -> // ``` -// fn foo(bar: B) {} +// fn foo(bar: B) {} // ``` pub(crate) fn replace_impl_trait_with_generic( acc: &mut Assists, @@ -59,7 +59,7 @@ mod tests { fn foo(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: B) {} + fn foo(bar: B) {} "#, ); } @@ -72,7 +72,7 @@ mod tests { fn foo(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: B) {} + fn foo(bar: B) {} "#, ); } @@ -85,7 +85,7 @@ mod tests { fn foo(foo: impl Foo, bar: <|>impl Bar) {} "#, r#" - fn foo(foo: impl Foo, bar: B) {} + fn foo(foo: impl Foo, bar: B) {} "#, ); } @@ -98,7 +98,7 @@ mod tests { fn foo<>(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: B) {} + fn foo(bar: B) {} "#, ); } @@ -112,7 +112,7 @@ mod tests { >(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: B) {} "#, ); @@ -127,7 +127,7 @@ mod tests { fn foo(bar: <|>impl Bar) {} "#, r#" - fn foo(bar: C) {} + fn foo(bar: C) {} "#, ); } @@ -147,7 +147,7 @@ mod tests { fn foo< G: Foo, F, - H, B: Bar, + H, B: Bar >(bar: B) {} "#, ); @@ -161,7 +161,7 @@ mod tests { fn foo(bar: <|>impl Foo + Bar) {} "#, r#" - fn foo(bar: F) {} + fn foo(bar: F) {} "#, ); } diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 4e5ca3825..27d15adb0 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs @@ -822,7 +822,7 @@ fn doctest_replace_impl_trait_with_generic() { fn foo(bar: <|>impl Bar) {} "#####, r#####" -fn foo(bar: B) {} +fn foo(bar: B) {} "#####, ) } -- cgit v1.2.3