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()
target,
|edit| {
let generic_letter = impl_trait_ty[..1].to_string();
- edit.replace_ast::<ast::Type>(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::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter))
+ .with_generic_params(generic_param_list);
+
edit.replace_ast(type_fn.clone(), new_type_fn);
},
)
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#"
"#,
);
}
+
+ #[test]
+ fn replace_impl_trait_without_generic_params() {
+ check_assist(
+ replace_impl_trait_with_generic,
+ r#"
+ fn foo(bar: <|>impl Bar) {}
+ "#,
+ r#"
+ fn foo<B: Bar>(bar: B) {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn replace_two_impl_trait_with_generic_params() {
+ check_assist(
+ replace_impl_trait_with_generic,
+ r#"
+ fn foo<G>(foo: impl Foo, bar: <|>impl Bar) {}
+ "#,
+ r#"
+ fn foo<G, B: Bar>(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<B: Bar>(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<B: Bar,
+ >(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<B>(bar: <|>impl Bar) {}
+ "#,
+ r#"
+ fn foo<B, C: Bar>(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) {}
+ "#,
+ );
+ }
}
ast::{
self,
make::{self, tokens},
- AstNode, TypeBoundsOwner,
+ AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner,
},
AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
to_insert.push(body.syntax().clone().into());
self.replace_children(single_node(old_body_or_semi), to_insert)
}
+
+ #[must_use]
+ pub fn with_generic_params(&self, generic_args: ast::GenericParamList) -> ast::Fn {
+ if let Some(old) = self.generic_param_list() {
+ return self.replace_descendant(old, generic_args);
+ }
+
+ let anchor = self.name().expect("The function must have a name").syntax().clone();
+
+ let mut to_insert: ArrayVec<[SyntaxElement; 1]> = ArrayVec::new();
+ to_insert.push(generic_args.syntax().clone().into());
+ self.insert_children(InsertPosition::After(anchor.into()), to_insert)
+ }
}
fn make_multiline<N>(node: N) -> N
impl ast::GenericParamList {
#[must_use]
- pub fn append_params(&self, params: impl IntoIterator<Item = ast::GenericParam>) -> Self {
+ pub fn append_params(
+ &self,
+ params: impl IntoIterator<Item = ast::GenericParam>,
+ ) -> ast::GenericParamList {
let mut res = self.clone();
params.into_iter().for_each(|it| res = res.append_param(it));
res
}
#[must_use]
- pub fn append_param(&self, item: ast::GenericParam) -> Self {
+ pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList {
let is_multiline = self.syntax().text().contains_char('\n');
let ws;
let space = if is_multiline {
};
let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
- to_insert.push(space.into());
+ if self.generic_params().next().is_some() {
+ to_insert.push(space.into());
+ }
to_insert.push(item.syntax().clone().into());
to_insert.push(make::token(T![,]).into());