aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/replace_impl_trait_with_generic.rs110
-rw-r--r--crates/syntax/src/ast/edit.rs26
2 files changed, 124 insertions, 12 deletions
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(
13 let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; 13 let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?;
14 let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?; 14 let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?;
15 15
16 let generic_param_list =
17 type_fn.generic_param_list().unwrap_or_else(|| make::generic_param_list(None));
18
19 let impl_trait_ty = type_impl_trait 16 let impl_trait_ty = type_impl_trait
20 .syntax() 17 .syntax()
21 .descendants() 18 .descendants()
@@ -31,11 +28,16 @@ pub(crate) fn replace_impl_trait_with_generic(
31 target, 28 target,
32 |edit| { 29 |edit| {
33 let generic_letter = impl_trait_ty[..1].to_string(); 30 let generic_letter = impl_trait_ty[..1].to_string();
34 edit.replace_ast::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter));
35 31
36 let new_params = generic_param_list 32 let generic_param_list = type_fn
37 .append_param(make::generic_param(generic_letter, Some(impl_trait_ty))); 33 .generic_param_list()
38 let new_type_fn = type_fn.replace_descendant(generic_param_list, new_params); 34 .unwrap_or_else(|| make::generic_param_list(None))
35 .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty)));
36
37 let new_type_fn = type_fn
38 .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter))
39 .with_generic_params(generic_param_list);
40
39 edit.replace_ast(type_fn.clone(), new_type_fn); 41 edit.replace_ast(type_fn.clone(), new_type_fn);
40 }, 42 },
41 ) 43 )
@@ -48,7 +50,7 @@ mod tests {
48 use crate::tests::check_assist; 50 use crate::tests::check_assist;
49 51
50 #[test] 52 #[test]
51 fn replace_with_generic_params() { 53 fn replace_impl_trait_with_generic_params() {
52 check_assist( 54 check_assist(
53 replace_impl_trait_with_generic, 55 replace_impl_trait_with_generic,
54 r#" 56 r#"
@@ -59,4 +61,96 @@ mod tests {
59 "#, 61 "#,
60 ); 62 );
61 } 63 }
64
65 #[test]
66 fn replace_impl_trait_without_generic_params() {
67 check_assist(
68 replace_impl_trait_with_generic,
69 r#"
70 fn foo(bar: <|>impl Bar) {}
71 "#,
72 r#"
73 fn foo<B: Bar>(bar: B) {}
74 "#,
75 );
76 }
77
78 #[test]
79 fn replace_two_impl_trait_with_generic_params() {
80 check_assist(
81 replace_impl_trait_with_generic,
82 r#"
83 fn foo<G>(foo: impl Foo, bar: <|>impl Bar) {}
84 "#,
85 r#"
86 fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}
87 "#,
88 );
89 }
90
91 #[test]
92 fn replace_impl_trait_with_empty_generic_params() {
93 check_assist(
94 replace_impl_trait_with_generic,
95 r#"
96 fn foo<>(bar: <|>impl Bar) {}
97 "#,
98 r#"
99 fn foo<B: Bar>(bar: B) {}
100 "#,
101 );
102 }
103
104 #[test]
105 fn replace_impl_trait_with_empty_multiline_generic_params() {
106 // FIXME: It would be more correct to place the generic parameter
107 // on the next line after the left angle.
108 check_assist(
109 replace_impl_trait_with_generic,
110 r#"
111 fn foo<
112 >(bar: <|>impl Bar) {}
113 "#,
114 r#"
115 fn foo<B: Bar,
116 >(bar: B) {}
117 "#,
118 );
119 }
120
121 #[test]
122 #[ignore = "This case is very rare but there is no simple solutions to fix it."]
123 fn replace_impl_trait_with_exist_generic_letter() {
124 check_assist(
125 replace_impl_trait_with_generic,
126 r#"
127 fn foo<B>(bar: <|>impl Bar) {}
128 "#,
129 r#"
130 fn foo<B, C: Bar>(bar: C) {}
131 "#,
132 );
133 }
134
135 #[test]
136 fn replace_impl_trait_with_multiline_generic_params() {
137 check_assist(
138 replace_impl_trait_with_generic,
139 r#"
140 fn foo<
141 G: Foo,
142 F,
143 H,
144 >(bar: <|>impl Bar) {}
145 "#,
146 r#"
147 fn foo<
148 G: Foo,
149 F,
150 H,
151 B: Bar,
152 >(bar: B) {}
153 "#,
154 );
155 }
62} 156}
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 1ccb4de6a..68987dbf6 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -13,7 +13,7 @@ use crate::{
13 ast::{ 13 ast::{
14 self, 14 self,
15 make::{self, tokens}, 15 make::{self, tokens},
16 AstNode, TypeBoundsOwner, 16 AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner,
17 }, 17 },
18 AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, 18 AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
19 SyntaxKind::{ATTR, COMMENT, WHITESPACE}, 19 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
@@ -46,6 +46,19 @@ impl ast::Fn {
46 to_insert.push(body.syntax().clone().into()); 46 to_insert.push(body.syntax().clone().into());
47 self.replace_children(single_node(old_body_or_semi), to_insert) 47 self.replace_children(single_node(old_body_or_semi), to_insert)
48 } 48 }
49
50 #[must_use]
51 pub fn with_generic_params(&self, generic_args: ast::GenericParamList) -> ast::Fn {
52 if let Some(old) = self.generic_param_list() {
53 return self.replace_descendant(old, generic_args);
54 }
55
56 let anchor = self.name().expect("The function must have a name").syntax().clone();
57
58 let mut to_insert: ArrayVec<[SyntaxElement; 1]> = ArrayVec::new();
59 to_insert.push(generic_args.syntax().clone().into());
60 self.insert_children(InsertPosition::After(anchor.into()), to_insert)
61 }
49} 62}
50 63
51fn make_multiline<N>(node: N) -> N 64fn make_multiline<N>(node: N) -> N
@@ -461,14 +474,17 @@ impl ast::MatchArmList {
461 474
462impl ast::GenericParamList { 475impl ast::GenericParamList {
463 #[must_use] 476 #[must_use]
464 pub fn append_params(&self, params: impl IntoIterator<Item = ast::GenericParam>) -> Self { 477 pub fn append_params(
478 &self,
479 params: impl IntoIterator<Item = ast::GenericParam>,
480 ) -> ast::GenericParamList {
465 let mut res = self.clone(); 481 let mut res = self.clone();
466 params.into_iter().for_each(|it| res = res.append_param(it)); 482 params.into_iter().for_each(|it| res = res.append_param(it));
467 res 483 res
468 } 484 }
469 485
470 #[must_use] 486 #[must_use]
471 pub fn append_param(&self, item: ast::GenericParam) -> Self { 487 pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList {
472 let is_multiline = self.syntax().text().contains_char('\n'); 488 let is_multiline = self.syntax().text().contains_char('\n');
473 let ws; 489 let ws;
474 let space = if is_multiline { 490 let space = if is_multiline {
@@ -482,7 +498,9 @@ impl ast::GenericParamList {
482 }; 498 };
483 499
484 let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); 500 let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
485 to_insert.push(space.into()); 501 if self.generic_params().next().is_some() {
502 to_insert.push(space.into());
503 }
486 to_insert.push(item.syntax().clone().into()); 504 to_insert.push(item.syntax().clone().into());
487 to_insert.push(make::token(T![,]).into()); 505 to_insert.push(make::token(T![,]).into());
488 506