]> git.lizzy.rs Git - rust.git/commitdiff
Resolve most of corner cases
authorAleksei Sidorov <gorthauer87@yandex.ru>
Thu, 3 Sep 2020 11:46:28 +0000 (14:46 +0300)
committerAleksei Sidorov <gorthauer87@yandex.ru>
Thu, 3 Sep 2020 14:46:04 +0000 (17:46 +0300)
crates/assists/src/handlers/replace_impl_trait_with_generic.rs
crates/syntax/src/ast/edit.rs

index 8af2d16dd8b5f47ebf819ac2834446a4d865ab24..5b0d5d97103bd3bf37423b60c3e0f4ca97742cd7 100644 (file)
@@ -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::<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);
         },
     )
@@ -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 @@ fn foo<G, B: Bar>(bar: B) {}
             "#,
         );
     }
+
+    #[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) {}
+            "#,
+        );
+    }
 }
index 1ccb4de6af8e23f6c4be4aecb57614961d85897e..68987dbf6c50bbac6c5f56be7d6b1768e728525e 100644 (file)
@@ -13,7 +13,7 @@
     ast::{
         self,
         make::{self, tokens},
-        AstNode, TypeBoundsOwner,
+        AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner,
     },
     AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
     SyntaxKind::{ATTR, COMMENT, WHITESPACE},
@@ -46,6 +46,19 @@ pub fn with_body(&self, body: ast::BlockExpr) -> ast::Fn {
         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
@@ -461,14 +474,17 @@ pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList {
 
 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 {
@@ -482,7 +498,9 @@ pub fn append_param(&self, item: ast::GenericParam) -> Self {
         };
 
         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());