use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner}; use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; // Assist: replace_impl_trait_with_generic // // Replaces `impl Trait` function argument with the named generic. // // ``` // fn foo(bar: $0impl Bar) {} // ``` // -> // ``` // fn foo(bar: B) {} // ``` pub(crate) fn replace_impl_trait_with_generic( acc: &mut Assists, ctx: &AssistContext, ) -> Option<()> { let impl_trait_type = ctx.find_node_at_offset::()?; let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?; let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?; let type_bound_list = impl_trait_type.type_bound_list()?; let target = fn_.syntax().text_range(); acc.add( AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), "Replace impl trait with generic", target, |edit| { let type_param_name = suggest_name::generic_parameter(&impl_trait_type); let generic_param_list = fn_ .generic_param_list() .unwrap_or_else(|| make::generic_param_list(None)) .append_param(make::generic_param(&type_param_name, Some(type_bound_list))); let new_type_fn = fn_ .replace_descendant::(impl_trait_type.into(), make::ty(&type_param_name)) .with_generic_param_list(generic_param_list); edit.replace_ast(fn_.clone(), new_type_fn); }, ) } #[cfg(test)] mod tests { use super::*; use crate::tests::check_assist; #[test] fn replace_impl_trait_with_generic_params() { check_assist( replace_impl_trait_with_generic, r#"fn foo(bar: $0impl Bar) {}"#, r#"fn foo(bar: B) {}"#, ); } #[test] fn replace_impl_trait_without_generic_params() { check_assist( replace_impl_trait_with_generic, r#"fn foo(bar: $0impl 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: $0impl 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: $0impl Bar) {}"#, r#"fn foo(bar: B) {}"#, ); } #[test] fn replace_impl_trait_with_empty_multiline_generic_params() { check_assist( replace_impl_trait_with_generic, r#" fn foo< >(bar: $0impl 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: $0impl 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: $0impl Bar) {} "#, r#" fn foo< G: Foo, F, H, B: Bar >(bar: B) {} "#, ); } #[test] fn replace_impl_trait_multiple() { check_assist( replace_impl_trait_with_generic, r#"fn foo(bar: $0impl Foo + Bar) {}"#, r#"fn foo(bar: F) {}"#, ); } }